From 9fbf291a2075c2884faa7e19468f5a7b850b5d40 Mon Sep 17 00:00:00 2001 From: Ray Andrew Date: Sat, 8 Mar 2025 23:50:50 -0600 Subject: [PATCH] add emacs configuration --- flake.nix | 8 +- home/default.nix | 90 ++- home/emacs/config/.gitignore | 2 + home/emacs/config/lib/naysayer-theme.el | 182 ++++++ home/emacs/config/lib/witness | 53 ++ home/emacs/config/post-init.el | 609 ++++++++++++++++++ home/emacs/config/pre-early-init.el | 26 + home/emacs/config/pre-init.el | 56 ++ home/emacs/default.nix | 26 + home/email/default.nix | 1 + home/shell.nix | 59 ++ hosts/default.nix | 1 + hosts/lemur/default.nix | 1 + hosts/pickwick/default.nix | 1 + lib.nix | 67 ++ nixos/default.nix | 112 ++-- nixos/nix.nix | 138 ++++ overlays/default.nix | 27 + packages/default.nix | 10 + packages/minimal-emacs-d/.gitignore | 1 + .../030825-init-file-debug.patch | 13 + packages/minimal-emacs-d/default.nix | 35 + packages/minimal-emacs-d/pre-early-init.el | 13 + 23 files changed, 1466 insertions(+), 65 deletions(-) create mode 100644 home/emacs/config/.gitignore create mode 100644 home/emacs/config/lib/naysayer-theme.el create mode 100644 home/emacs/config/lib/witness create mode 100644 home/emacs/config/post-init.el create mode 100644 home/emacs/config/pre-early-init.el create mode 100644 home/emacs/config/pre-init.el create mode 100644 home/emacs/default.nix create mode 100644 home/shell.nix create mode 100644 lib.nix create mode 100644 nixos/nix.nix create mode 100644 overlays/default.nix create mode 100644 packages/default.nix create mode 100644 packages/minimal-emacs-d/.gitignore create mode 100644 packages/minimal-emacs-d/030825-init-file-debug.patch create mode 100644 packages/minimal-emacs-d/default.nix create mode 100644 packages/minimal-emacs-d/pre-early-init.el diff --git a/flake.nix b/flake.nix index d869b80..bea401e 100644 --- a/flake.nix +++ b/flake.nix @@ -36,7 +36,11 @@ inherit system; config.allowUnfree = true; }; - lib = nixpkgs.lib; + lib = import ./lib.nix { + inherit (nixpkgs) lib; + inherit pkgs; + inherit (inputs) home-manager; + }; createCommonArgs = system: { inherit self @@ -122,6 +126,8 @@ } ); + packages = forAllSystems (commonArgs': (import ./packages commonArgs')); + formatter = forAllSystems ({ system, ... }: treefmtEval.${system}.config.build.wrapper); }; diff --git a/home/default.nix b/home/default.nix index b408dd7..5e62684 100644 --- a/home/default.nix +++ b/home/default.nix @@ -8,29 +8,85 @@ }: { imports = [ ./email - ./ssh + ./emacs ./git.nix ./gui.nix ./latex.nix + ./ssh + ./shell.nix ]; - programs.neovim.enable = true; - home.packages = with pkgs; [ - gpclient - openconnect - emacs - fd - ripgrep - yazi - tree - gnumake - texinfo - ]; + config = { + home.packages = + with pkgs; + [ + fd + fx + jq + sd + ugrep + unzip + gpclient + openconnect + fd + ripgrep + yazi + tree + gnumake + texinfo + ] + ++ (lib.attrValues config.custom.shell.packages); - fonts.fontconfig.enable = true; + services = { + gpg-agent = { + enable = true; + # enableSshSupport = true; + }; + }; - programs.home-manager.enable = true; + fonts.fontconfig.enable = true; - # https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion - home.stateVersion = "25.05"; + programs = { + home-manager.enable = true; + neovim.enable = true; + bash = { + enable = true; + enableVteIntegration = true; + }; + gpg = { + enable = true; + }; + bat = { + enable = true; + extraPackages = [ + (pkgs.symlinkJoin { + name = "batman"; + paths = [ pkgs.bat-extras.batman ]; + postBuild = '' + mkdir -p $out/share/bash-completion/completions + echo 'complete -F _comp_cmd_man batman' > $out/share/bash-completion/completions/batman + + mkdir -p $out/share/fish/vendor_completions.d + echo 'complete batman --wraps man' > $out/share/fish/vendor_completions.d/batman.fish + + mkdir -p $out/share/zsh/site-functions + cat << EOF > $out/share/zsh/site-functions/_batman + #compdef batman + _man "$@" + EOF + ''; + meta.mainProgram = "batman"; + }) + ]; + }; + fzf = { + enable = true; + enableBashIntegration = config.programs.bash.enable; + enableFishIntegration = config.programs.fish.enable; + }; + }; + + # https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion + home.stateVersion = "25.05"; + }; } diff --git a/home/emacs/config/.gitignore b/home/emacs/config/.gitignore new file mode 100644 index 0000000..b650c16 --- /dev/null +++ b/home/emacs/config/.gitignore @@ -0,0 +1,2 @@ +var +undo diff --git a/home/emacs/config/lib/naysayer-theme.el b/home/emacs/config/lib/naysayer-theme.el new file mode 100644 index 0000000..d9492f2 --- /dev/null +++ b/home/emacs/config/lib/naysayer-theme.el @@ -0,0 +1,182 @@ +;;; naysayer-theme.el --- The naysayer color theme + +;; Author: Nick Aversano +;; Version: 0.33 +;; Filename: naysayer-theme.el +;; Package-Requires: ((emacs "24")) +;; URL: https://github.com/nickav/naysayer-theme.el +;; License: GPL-3+ + +;;; Commentary: + +;; Dark green blue color scheme with tan colors. Inspired by Jonathan Blow's compiler livestreams. + +;;; Code: + +(unless (>= emacs-major-version 24) + (error "The naysayer theme requires Emacs 24 or later!")) + +(deftheme naysayer "The naysayer color theme") + +;; Monokai colors +(defcustom naysayer-theme-yellow "#E6DB74" "Primary colors - yellow" :type 'string :group 'monokai) +(defcustom naysayer-theme-orange "#FD971F" "Primary colors - orange" :type 'string :group 'monokai) +(defcustom naysayer-theme-red "#F92672" "Primary colors - red" :type 'string :group 'monokai) +(defcustom naysayer-theme-magenta "#FD5FF0" "Primary colors - magenta" :type 'string :group 'monokai) +(defcustom naysayer-theme-blue "#66D9EF" "Primary colors - blue" :type 'string :group 'monokai) +(defcustom naysayer-theme-green "#A6E22E" "Primary colors - green" :type 'string :group 'monokai) +(defcustom naysayer-theme-cyan "#A1EFE4" "Primary colors - cyan" :type 'string :group 'monokai) +(defcustom naysayer-theme-violet "#AE81FF" "Primary colors - violet" :type 'string :group 'monokai) + +(let ((background "#072626") + (gutters "#072626") + (gutter-fg "#d3b58d") + (gutters-active "#062329") + (builtin "#ffffff") + (selection "blue") + (text "#d3b58d") + (comments "#3fdf1f") + (punctuation "#8cde94") + (keywords "#ffffff") + (variables "#d3b58d") + (functions "#ffffff") + (methods "#ffffff") + (strings "#0fdfaf") + (constants "#79ffcf") + (macros "lightgreen") + (numbers "#79ffcf") + (white "#ffffff") + (error "#504038") + (warning "#504038") + (highlight-line "#0b3335") + (line-fg "#126367")) + + (custom-theme-set-faces + 'naysayer + + ;; Default colors + ;; ***************************************************************************** + + `(default ((t (:foreground ,text :background ,background, :weight normal)))) + `(region ((t (:foreground nil :background ,selection)))) + `(cursor ((t (:background ,white )))) + `(fringe ((t (:background ,background :foreground ,white)))) + `(linum ((t (:background ,background :foreground ,gutter-fg)))) + `(highlight ((t (:foreground nil :background ,selection)))) + + ;; Font lock faces + ;; ***************************************************************************** + + `(font-lock-keyword-face ((t (:foreground ,keywords)))) + `(font-lock-type-face ((t (:foreground ,punctuation)))) + `(font-lock-constant-face ((t (:foreground ,constants)))) + `(font-lock-variable-name-face ((t (:foreground ,variables)))) + `(font-lock-builtin-face ((t (:foreground ,builtin)))) + `(font-lock-string-face ((t (:foreground ,strings)))) + `(font-lock-comment-face ((t (:foreground ,comments)))) + `(font-lock-comment-delimiter-face ((t (:foreground ,comments)))) + `(font-lock-doc-face ((t (:foreground ,comments)))) + `(font-lock-function-name-face ((t (:foreground ,functions)))) + `(font-lock-doc-string-face ((t (:foreground ,strings)))) + `(font-lock-preprocessor-face ((t (:foreground ,macros)))) + `(font-lock-warning-face ((t (:foreground ,warning)))) + + ;; Plugins + ;; ***************************************************************************** + `(trailing-whitespace ((t (:foreground nil :background ,warning)))) + `(whitespace-trailing ((t (:background nil :foreground ,warning :inverse-video t)))) + + `(linum ((t (:foreground ,line-fg :background ,background)))) + `(linum-relative-current-face ((t (:foreground ,white :background ,background)))) + `(line-number ((t (:foreground ,line-fg :background ,background)))) + `(line-number-current-line ((t (:foreground ,white :background ,background)))) + + ;; hl-line-mode + `(hl-line ((t (:background ,highlight-line)))) + `(hl-line-face ((t (:background ,highlight-line)))) + + ;; rainbow-delimiters + `(rainbow-delimiters-depth-1-face ((t (:foreground ,naysayer-theme-violet)))) + `(rainbow-delimiters-depth-2-face ((t (:foreground ,naysayer-theme-blue)))) + `(rainbow-delimiters-depth-3-face ((t (:foreground ,naysayer-theme-green)))) + `(rainbow-delimiters-depth-4-face ((t (:foreground ,naysayer-theme-yellow)))) + `(rainbow-delimiters-depth-5-face ((t (:foreground ,naysayer-theme-orange)))) + `(rainbow-delimiters-depth-6-face ((t (:foreground ,naysayer-theme-red)))) + `(rainbow-delimiters-depth-7-face ((t (:foreground ,naysayer-theme-violet)))) + `(rainbow-delimiters-depth-8-face ((t (:foreground ,naysayer-theme-blue)))) + `(rainbow-delimiters-depth-9-face ((t (:foreground ,naysayer-theme-green)))) + `(rainbow-delimiters-depth-10-face ((t (:foreground ,naysayer-theme-yellow)))) + `(rainbow-delimiters-depth-11-face ((t (:foreground ,naysayer-theme-orange)))) + `(rainbow-delimiters-depth-12-face ((t (:foreground ,naysayer-theme-red)))) + + ;; which-func + `(which-func ((t (:inverse-video unspecified + :underline unspecified + :foreground ,background + :weight bold + :box nil)))) + + ;; mode-line and powerline + `(mode-line-buffer-id ((t (:foreground ,background :distant-foreground ,text :text ,text :weight bold)))) + `(mode-line ((t (:inverse-video unspecified + :underline unspecified + :foreground ,background + :background ,text + :box nil)))) + `(powerline-active1 ((t (:background ,text :foreground ,background)))) + `(powerline-active2 ((t (:background ,text :foreground ,background)))) + + `(mode-line-inactive ((t (:inverse-video unspecified + :underline unspecified + :foreground ,text + :background ,background + :box nil)))) + `(powerline-inactive1 ((t (:background ,background :foreground ,text)))) + `(powerline-inactive2 ((t (:background ,background :foreground ,text)))) + + ;; better compatibility with default DOOM mode-line + `(error ((t (:foreground nil :weight normal)))) + `(doom-modeline-project-dir ((t (:foreground nil :weight bold)))) + + ;; js2-mode + `(js2-function-call ((t (:inherit (font-lock-function-name-face))))) + `(js2-function-param ((t (:foreground ,text)))) + `(js2-jsdoc-tag ((t (:foreground ,keywords)))) + `(js2-jsdoc-type ((t (:foreground ,constants)))) + `(js2-jsdoc-value((t (:foreground ,text)))) + `(js2-object-property ((t (:foreground ,text)))) + `(js2-external-variable ((t (:foreground ,constants)))) + `(js2-error ((t (:foreground ,error)))) + `(js2-warning ((t (:foreground ,warning)))) + + ;; highlight numbers + `(highlight-numbers-number ((t (:foreground ,numbers)))) + + ;; tab-bar-mode + `(tab-bar ((t (:inherit modeline)))) + `(tab-bar-tab ((t (:foreground ,background :background ,text)))) + `(tab-bar-tab-inactive ((t (:foreground ,text :background ,background)))) + ) + + (custom-theme-set-variables + 'naysayer + '(linum-format " %5i ") + ) +) + +;;;###autoload +(when (and (boundp 'custom-theme-load-path) load-file-name) + (add-to-list 'custom-theme-load-path + (file-name-as-directory (file-name-directory load-file-name)))) + +;; ***************************************************************************** + +(provide-theme 'naysayer) + +;; Local Variables: +;; no-byte-compile: t +;; End: + +(provide 'naysayer-theme) + +;;; naysayer-theme.el ends here diff --git a/home/emacs/config/lib/witness b/home/emacs/config/lib/witness new file mode 100644 index 0000000..8008c6c --- /dev/null +++ b/home/emacs/config/lib/witness @@ -0,0 +1,53 @@ +{ + "name": "Witness Color Scheme", + "globals": { + "accent": "#d3b58d", + "background": "#072626", + "block_caret": "rgba(144, 238, 144, 0.6)", + "brackets_options": "underline", + "caret": "rgba(144, 238, 144, 1)", + "fold_marker": "#d3b58d", + "foreground": "#d3b58d", + "gutter": "#072626", + "gutter_foreground": "#d3b58d", + "inactive_selection": "#0049f5", + "line_highlight": "#072626", + "selection": "blue", + "tags_options": "underline" + }, + "rules": [ + { + "scope": "comment", + "foreground": "#3fdf1f" + }, + { + "scope": "constant.language", + "foreground": "#79ffcf" + }, + { + "scope": "entity.name, keyword.declaration, meta.annotation, meta.path, meta.struct meta.block, meta.tag, support.variable, variable, variable.other.readwrite", + "foreground": "lightblue" + }, + { + "scope": "entity.name.constant, keyword.control, meta.class entity.name.class, meta.constant, meta.function.parameters, meta.struct entity.name.struct, meta.struct meta.group, storage.type, support.constant, support.type, variable.type", + "foreground": "lightgreen" + }, + { + "scope": "entity.name.enum, keyword.operator, meta.block meta.method entity.name.function, meta.block storage.modifier, meta.class storage.type, meta.function-call variable.function, meta.preprocessor, meta.struct storage.type - meta.block, meta.template storage.type, punctuation.section, variable.other", + "foreground": "#d3b58d" + }, + { + "scope": "entity.name.function, keyword, storage", + "foreground": "white" + }, + { + "scope": "invalid", + "foreground": "darkseagreen", + "background": "#504038" + }, + { + "scope": "string", + "foreground": "#0fdfaf" + } + ] +} diff --git a/home/emacs/config/post-init.el b/home/emacs/config/post-init.el new file mode 100644 index 0000000..19beaf6 --- /dev/null +++ b/home/emacs/config/post-init.el @@ -0,0 +1,609 @@ +;;; post-init.el --- Post Initalization -*- no-byte-compile: t; lexical-binding: t; -*- + +;; Auto-revert in Emacs is a feature that automatically updates the +;; contents of a buffer to reflect changes made to the underlying file +;; on disk. +(add-hook 'elpaca-after-init-hook #'global-auto-revert-mode) + +;; recentf is an Emacs package that maintains a list of recently +;; accessed files, making it easier to reopen files you have worked on +;; recently. +(add-hook 'elpaca-after-init-hook #'(lambda() + (let ((inhibit-message t)) + (recentf-mode 1)))) +(add-hook 'kill-emacs-hook #'recentf-cleanup) + +;; savehist is an Emacs feature that preserves the minibuffer history between +;; sessions. It saves the history of inputs in the minibuffer, such as commands, +;; search strings, and other prompts, to a file. This allows users to retain +;; their minibuffer history across Emacs restarts. +(add-hook 'elpaca-after-init-hook #'savehist-mode) + +;; save-place-mode enables Emacs to remember the last location within a file +;; upon reopening. This feature is particularly beneficial for resuming work at +;; the precise point where you previously left off. +(add-hook 'elpaca-after-init-hook #'save-place-mode) + +(use-package emacs + :elpaca nil + :ensure nil + :custom + (delete-selection-mode t) ;; Select text and delete it by typing. + (electric-indent-mode nil) ;; Turn off the weird indenting that Emacs does by default. + (electric-pair-mode t) ;; Turns on automatic parens pairing + + (blink-cursor-mode nil) ;; Don't blink cursor + (global-auto-revert-mode t) ;; Automatically reload file and show changes if the file has changed + (indent-tabs-mode nil) + + ;;(dired-kill-when-opening-new-dired-buffer t) ;; Dired don't create new buffer + (recentf-mode t) ;; Enable recent file mode + (recentf-max-saved-items 50) + + ;;(global-visual-line-mode t) ;; Enable truncated lines + ;;(display-line-numbers-type 'relative) ;; Relative line numbers + (global-display-line-numbers-mode t) ;; Display line numbers + (split-width-threshold nil) ;; Splitting behavior + :bind (([escape] . keyboard-escape-quit) + ("C-x c" . compile))) + +;; ========================= +;; Theme +;; ========================= + +(use-package naysayer-theme + :ensure nil + ;; :defer t + ;; :elpaca nil + :config + (load-theme 'naysayer t) + :load-path rs/lib-dir) + +(global-font-lock-mode 1) +(set-cursor-color "lightgreen") +(set-background-color "#072626") +;; (global-set-key [C-return] 'save-buffer) + +;(set-face-attribute 'default nil :font "Anonymous Pro-14") +;; (set-face-attribute 'default nil :font "Consolas-174") + +(set-face-foreground 'font-lock-builtin-face "lightgreen") + +;; ========================= +;; Better UX +;; ========================= + +(use-package vertico + ;; (Note: It is recommended to also enable the savehist package.) + :ensure t + :defer t + :commands (vertico-mode vertico-multiform-mode) + :custom + (vertico-cycle t) + (vertico-multiform-commands + '((consult-imenu buffer indexed) + (find-file flat (vertico-cycle . t)) + (execute-extended-command flat (vertico-cycle . t)))) + (vertico-multiform-categories + '((consult-grep buffer))) + :hook + (elpaca-after-init . vertico-mode) + (elpaca-after-init . vertico-multiform-mode)) + +(use-package orderless + ;; Vertico leverages Orderless' flexible matching capabilities, allowing users + ;; to input multiple patterns separated by spaces, which Orderless then + ;; matches in any order against the candidates. + :ensure t + :custom + (completion-styles '(orderless basic)) + (completion-category-defaults nil) + (completion-category-overrides '((file (styles partial-completion))))) + +(use-package marginalia + ;; Marginalia allows Embark to offer you preconfigured actions in more contexts. + ;; In addition to that, Marginalia also enhances Vertico by adding rich + ;; annotations to the completion candidates displayed in Vertico's interface. + :ensure t + :defer t + :commands (marginalia-mode marginalia-cycle) + :hook (elpaca-after-init . marginalia-mode)) + +(use-package embark + ;; Embark is an Emacs package that acts like a context menu, allowing + ;; users to perform context-sensitive actions on selected items + ;; directly from the completion interface. + :ensure t + :defer t + :commands (embark-act + embark-dwim + embark-export + embark-collect + embark-bindings + embark-prefix-help-command) + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + (setq prefix-help-command #'embark-prefix-help-command) + + :config + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +(use-package embark-consult + :ensure t + :hook + (embark-collect-mode . consult-preview-at-point-mode)) + +(use-package consult + :ensure t + :bind (;; C-c bindings in `mode-specific-map' + ("C-c M-x" . consult-mode-command) + ("C-c h" . consult-history) + ("C-c k" . consult-kmacro) + ("C-c m" . consult-man) + ("C-c i" . consult-info) + ([remap Info-search] . consult-info) + ;; C-x bindings in `ctl-x-map' + ("C-x M-:" . consult-complex-command) + ("C-x b" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ("C-x t b" . consult-buffer-other-tab) + ("C-x r b" . consult-bookmark) + ("C-x p b" . consult-project-buffer) + ;; Custom M-# bindings for fast register access + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) + ("C-M-#" . consult-register) + ;; Other custom bindings + ("M-y" . consult-yank-pop) + ;; M-g bindings in `goto-map' + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) + ("M-g g" . consult-goto-line) + ("M-g M-g" . consult-goto-line) + ("M-g o" . consult-outline) + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ("M-g i" . consult-imenu) + ("M-g I" . consult-imenu-multi) + ;; M-s bindings in `search-map' + ("M-s d" . consult-find) + ("M-s c" . consult-locate) + ("M-s g" . consult-grep) + ("M-s G" . consult-git-grep) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s k" . consult-keep-lines) + ("M-s u" . consult-focus-lines) + ;; Isearch integration + ("M-s e" . consult-isearch-history) + :map isearch-mode-map + ("M-e" . consult-isearch-history) + ("M-s e" . consult-isearch-history) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ;; Minibuffer history + :map minibuffer-local-map + ("M-s" . consult-history) + ("M-r" . consult-history)) + + ;; Enable automatic preview at point in the *Completions* buffer. + :hook (completion-list-mode . consult-preview-at-point-mode) + + :init + ;; Optionally configure the register formatting. This improves the register + (setq register-preview-delay 0.5 + register-preview-function #'consult-register-format) + + ;; Optionally tweak the register preview window. + (advice-add #'register-preview :override #'consult-register-window) + + ;; Use Consult to select xref locations with preview + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + + :config + (consult-customize + consult-theme :preview-key '(:debounce 0.2 any) + consult-ripgrep consult-git-grep consult-grep + consult-bookmark consult-recent-file consult-xref + consult--source-bookmark consult--source-file-register + consult--source-recent-file consult--source-project-recent-file + ;; :preview-key "M-." + :preview-key '(:debounce 0.4 any)) + (setq consult-narrow-key "<")) + +;; Outline Indent +(use-package outline-indent + :ensure t + :defer t + :commands outline-indent-minor-mode + + :init + ;; The minor mode can also be automatically activated for a certain modes. + ;; For example for Python and YAML: + (add-hook 'python-mode-hook #'outline-indent-minor-mode) + (add-hook 'python-ts-mode-hook #'outline-indent-minor-mode) + + (add-hook 'yaml-mode-hook #'outline-indent-minor-mode) + (add-hook 'yaml-ts-mode-hook #'outline-indent-minor-mode) + + :custom + (outline-indent-ellipsis " ▼ ")) + +;; Dired + +(defun rs/dired-copy-path-at-point () + (interactive) + (dired-copy-filename-as-kill 0)) + +(use-package dired + :ensure nil + :bind (("C-x -" . dired-jump) + ("C-x e" . dired-jump) + (:map dired-mode-map + ("-" . dired-up-directory) + ("W" . rs/dired-copy-path-at-point))) + :custom + (dired-deletion-confirmer #'y-or-n-p) + (dired-dwim-target t) + ;; (dired-listing-switches "-Alh") + (dired-listing-switches "-AlhcF --group-directories-first") + (dired-auto-revert-buffer t)) + +(use-package dired-x + :after dired + :ensure nil) + +;; Dired buffers: Automatically hide file details (permissions, size, +;; modification date, etc.) and all the files in the `dired-omit-files' regular +;; expression for a cleaner display. +(add-hook 'dired-mode-hook #'dired-hide-details-mode) + +;; Hide files from dired +(setq dired-omit-files (concat "\\`[.]\\'" + "\\|\\(?:\\.js\\)?\\.meta\\'" + "\\|\\.\\(?:elc|a\\|o\\|pyc\\|pyo\\|swp\\|class\\)\\'" + "\\|^\\.DS_Store\\'" + "\\|^\\.\\(?:svn\\|git\\)\\'" + "\\|^\\.ccls-cache\\'" + "\\|^__pycache__\\'" + "\\|^\\.project\\(?:ile\\)?\\'" + "\\|^flycheck_.*" + "\\|^flymake_.*")) +(add-hook 'dired-mode-hook #'dired-omit-mode) + +;; Undo Tree +(use-package undo-tree + :ensure t + :defer t + ;; :demand t + :diminish + :bind (("C-M-u" . undo-tree-visualize) + ("M-_" . undo-tree-redo) + ("M--" . go-back-to-last-edit) + :map undo-tree-map + (("C-x u" . nil))) + :custom + (undo-tree-visualizer-diff t) + (undo-tree-enable-undo-in-region t) + ;; (undo-tree-auto-save-history nil) + (undo-tree-history-directory-alist `((".*" . ,(expand-file-name "undo" rs/emacs-dir)))) + ;; (setq undo-tree-visualizer-relative-timestamps nil) + ;; (setq undo-tree-visualizer-timestamps t) + :config + (defun go-back-to-last-edit () + "Jump back to the last change in the current buffer." + (interactive) + (ignore-errors + (let ((inhibit-message t)) + (undo-tree-undo) + (undo-tree-redo)))) + + (defun undo-tree-split-side-by-side (original-function &rest args) + "Split undo-tree side-by-side" + (let ((split-height-threshold nil) + (split-width-threshold 0)) + (apply original-function args))) + + (advice-add 'undo-tree-visualize :around #'undo-tree-split-side-by-side) + :hook + (elpaca-after-init . global-undo-tree-mode)) + +(use-package multiple-cursors + :bind (("C-S-c C-S-c" . mc/edit-lines) + ("C-M-SPC" . set-rectangular-region-anchor) + ("C->" . mc/mark-next-like-this) + ("C-<" . mc/mark-previous-like-this) + ("C-c C-<" . mc/mark-all-like-this) + ("C-\"" . mc/skip-to-next-like-this) + ("C-:" . mc/skip-to-previous-like-this))) + +(use-package move-text + :bind (("M-p" . move-text-up) + ("M-n" . move-text-down))) + +;; ========================= +;; Magit +;; ========================= + +(use-package magit + :commands magit-status + :bind ("C-x g" . magit)) + +;; ========================= +;; Programming Languages +;; ========================= + +(use-package nix-mode + :mode "\\.nix\\'") + +(use-package python-mode + :mode ("\\.py\\'" . python-mode)) + +(use-package markdown-mode + :mode ("\\.md\\'" . markdown-mode)) + +(use-package lua-mode + :mode ("\\.lua\\'" . lua-mode)) + +(use-package yaml-mode + :mode (("\\.yml\\'" . yaml-mode) + ("\\.yaml\\'" . yaml-mode))) + +(use-package yasnippet + :commands (yas-minor-modeb yas-reload-all) + :config + (yas-reload-all) + :hook + ;; (elpaca-after-init . yas-reload-all) + (prog-mode . yas-minor-mode)) + +(use-package yasnippet-snippets + :after yasnippet) + +(use-package clipetty + :hook (elpaca-after-init . global-clipetty-mode)) + +;; ========================= +;; Miscellaneous +;; ========================= + +(global-unset-key (kbd "C-M-_")) ;; unbind redo-undo + +(use-package diminish) + +(use-package rainbow-mode + :commands (rainbow-mode)) + +(use-package rainbow-delimiters + :hook (prog-mode . rainbow-delimiters-mode)) + +(use-package term-keys + :ensure (:host github :repo "CyberShadow/term-keys") + :config (unless (display-graphic-p) (term-keys-mode t))) + +(use-package windmove + :elpaca nil + :bind (("C-c " . windmove-left) + ("C-c " . windmove-right) + ("C-c " . windmove-up) + ("C-c " . windmove-down))) + +(use-package ace-window + :defer t + :commands (ace-window) + :bind ("C-x C-o" . ace-window)) + +(use-package compat) +(use-package transient) + +(use-package compile-angel + :diminish + :ensure t + :demand t + :config + ;; Set `compile-angel-verbose` to nil to suppress output from compile-angel. + ;; Drawback: The minibuffer will not display compile-angel's actions. + (setq compile-angel-verbose nil) + (compile-angel-on-load-mode) + (add-hook 'emacs-lisp-mode-hook #'compile-angel-on-save-local-mode)) + +(use-package which-key + ;; :elpaca nil + :defer t + :commands which-key-mode + :hook (elpaca-after-init . which-key-mode) + :custom + (which-key-idle-delay 1.5) + (which-key-idle-secondary-delay 0.25) + (which-key-add-column-padding 1) + (which-key-max-description-length 40)) + +(unless (and (eq window-system 'mac) + (bound-and-true-p mac-carbon-version-string)) + ;; Enables `pixel-scroll-precision-mode' on all operating systems and Emacs + ;; versions, except for emacs-mac. + ;; + ;; Enabling `pixel-scroll-precision-mode' is unnecessary with emacs-mac, as + ;; this version of Emacs natively supports smooth scrolling. + ;; https://bitbucket.org/mituharu/emacs-mac/commits/65c6c96f27afa446df6f9d8eff63f9cc012cc738 + (setq pixel-scroll-precision-use-momentum nil) ; Precise/smoother scrolling + (pixel-scroll-precision-mode 1)) + +(unless (display-graphic-p) + (xterm-mouse-mode 1) + (global-set-key (kbd "") 'scroll-down-line) + (global-set-key (kbd "") 'scroll-up-line)) + +;; Display the time in the modeline +(display-time-mode 1) + +;; Paren match highlighting +(show-paren-mode 1) + +;; Track changes in the window configuration, allowing undoing actions such as +;; closing windows. +(winner-mode 1) + +;; Replace selected text with typed text +(delete-selection-mode 1) + +;; Configure Emacs to ask for confirmation before exiting +(setq confirm-kill-emacs 'y-or-n-p) + +(use-package uniquify + :ensure nil + :custom + (uniquify-buffer-name-style 'reverse) + (uniquify-separator "•") + (uniquify-after-kill-buffer-p t) + (uniquify-ignore-buffers-re "^\\*")) + +;; Window dividers separate windows visually. Window dividers are bars that can +;; be dragged with the mouse, thus allowing you to easily resize adjacent +;; windows. +;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Window-Dividers.html +(add-hook 'elpaca-after-init-hook #'window-divider-mode) + +;; Enable on-the-fly spell checking (Flyspell mode). +(add-hook 'text-mode-hook #'flyspell-mode) + +;; Change split behavior + +(defun delete-window-evenly () + (interactive) + (command-execute 'delete-window) + (command-execute 'balance-windows)) +(global-set-key (kbd "C-x 0") 'delete-window-evenly) + +(defun split-vertical-evenly () + (interactive) + (command-execute 'split-window-vertically) + (command-execute 'balance-windows)) +(global-set-key (kbd "C-x 2") 'split-vertical-evenly) + +(defun split-horizontal-evenly () + (interactive) + (command-execute 'split-window-horizontally) + (command-execute 'balance-windows)) +(global-set-key (kbd "C-x 3") 'split-horizontal-evenly) + +;; Increase/Decrease Font + +(defun rs/zoom-frame (&optional amt frame) + "Increaze FRAME font size by amount AMT. Defaults to selected +frame if FRAME is nil, and to 1 if AMT is nil." + (interactive "p") + (let* ((frame (or frame (selected-frame))) + (font (face-attribute 'default :font frame)) + (size (font-get font :size)) + (amt (or amt 1)) + (new-size (+ size amt))) + (set-frame-font (font-spec :size new-size) t `(,frame)) + (message "Frame's font new size: %d" new-size))) + +(defun rs/zoom-frame-out (&optional amt frame) + "Call `rs/zoom-frame' with negative argument." + + (interactive "p") + (rs/zoom-frame (- (or amt 1)) frame)) + +(global-set-key (kbd "C-=") 'rs/zoom-frame) +(global-set-key (kbd "C--") 'rs/zoom-frame-out) +(global-set-key (kbd "") 'rs/zoom-frame) +(global-set-key (kbd "") 'rs/zoom-frame-out) +(global-set-key (kbd "") 'rs/zoom-frame) +(global-set-key (kbd "") 'rs/zoom-frame-out) + +;; ========================= +;; Org +;; ========================= + +;; Agenda variables +(setq org-directory "~/Documents/org/") ; Non-absolute paths for agenda and + ; capture templates will look here. + +(setq org-agenda-files '("inbox.org" "work.org")) + +;; Default tags +(setq org-tag-alist '( + ;; locale + (:startgroup) + ("home" . ?h) + ("work" . ?w) + ("school" . ?s) + (:endgroup) + (:newline) + ;; scale + (:startgroup) + ("one-shot" . ?o) + ("project" . ?j) + ("tiny" . ?t) + (:endgroup) + ;; misc + ("meta") + ("review") + ("reading"))) + +;; Org-refile: where should org-refile look? +(setq org-refile-targets 'FIXME) + +;;; Phase 3 variables + +;; Org-roam variables +(setq org-roam-directory "~/Documents/org-roam/") +(setq org-roam-index-file "~/Documents/org-roam/index.org") + +;;; Optional variables + +;; Advanced: Custom link types +;; This example is for linking a person's 7-character ID to their page on the +;; free genealogy website Family Search. +(setq org-link-abbrev-alist + '(("family_search" . "https://www.familysearch.org/tree/person/details/%s"))) + +(use-package org + :elpaca nil + :hook ((org-mode . visual-line-mode) ; wrap lines at word breaks + (org-mode . flyspell-mode)) ; spell checking! + + :bind (:map global-map + ("C-c l s" . org-store-link) ; Mnemonic: link → store + ("C-c l i" . org-insert-link-global)) ; Mnemonic: link → insert + :config + (require 'oc-csl) ; citation support + (add-to-list 'org-export-backends 'md) + (setf (cdr (assoc 'file org-link-frame-setup)) 'find-file) + (setq org-export-with-smart-quotes t) + (setq org-todo-keywords + '((sequence "TODO(t)" "WAITING(w@/!)" "STARTED(s!)" "|" "DONE(d!)" "OBSOLETE(o@)"))) + (setq org-outline-path-complete-in-steps nil) + (setq org-refile-use-outline-path 'file) + (setq org-capture-templates + '(("c" "Default Capture" entry (file "inbox.org") + "* TODO %?\n%U\n%i") + ;; Capture and keep an org-link to the thing we're currently working with + ("r" "Capture with Reference" entry (file "inbox.org") + "* TODO %?\n%U\n%i\n%a") + ;; Define a section + ("w" "Work") + ("wm" "Work meeting" entry (file+headline "work.org" "Meetings") + "** TODO %?\n%U\n%i\n%a") + ("wr" "Work report" entry (file+headline "work.org" "Reports") + "** TODO %?\n%U\n%i\n%a"))) + (setq org-agenda-custom-commands + '(("n" "Agenda and All Todos" + ((agenda) + (todo))) + ("w" "Work" agenda "" + ((org-agenda-files '("work.org"))))))) diff --git a/home/emacs/config/pre-early-init.el b/home/emacs/config/pre-early-init.el new file mode 100644 index 0000000..a21a7b6 --- /dev/null +++ b/home/emacs/config/pre-early-init.el @@ -0,0 +1,26 @@ +;;; pre-early-init.el --- Pre Early Initalization -*- no-byte-compile: t; lexical-binding: t; -*- + +(defconst rs/emacs-dir minimal-emacs-user-directory) +(defconst rs/lib-dir (concat (expand-file-name "lib" minimal-emacs-user-directory) "/")) + +;; Reducing clutter in ~/.emacs.d by redirecting files to ~/emacs.d/var/ +(setq minimal-emacs-var-dir (expand-file-name "var/" minimal-emacs-user-directory)) +(setq package-user-dir (expand-file-name "elpa" minimal-emacs-var-dir)) +(setq user-emacs-directory minimal-emacs-var-dir) + +;; By default, minimal-emacs-package-initialize-and-refresh is set to t, which +;; makes minimal-emacs.d call the built-in package manager. Since Elpaca will +;; replace the package manager, there is no need to call it. +(setq minimal-emacs-package-initialize-and-refresh nil) + +(setq minimal-emacs-gc-cons-threshold (* 64 1024 1024)) + +(setq custom-file null-device) + +;; disable screen flashing because bg color hasn't initialized yet from theme +;; (setq default-frame-alist '((background-color . "#062329") +;; (ns-appearance . dark) +;; (ns-transparent-titlebar . t))) +(setq default-frame-alist '((background-color . "#072626") + (ns-appearance . dark) + (ns-transparent-titlebar . t))) diff --git a/home/emacs/config/pre-init.el b/home/emacs/config/pre-init.el new file mode 100644 index 0000000..a1dbb39 --- /dev/null +++ b/home/emacs/config/pre-init.el @@ -0,0 +1,56 @@ +;;; pre-init.el --- Pre Initalization -*- no-byte-compile: t; lexical-binding: t; -*- + +(defvar elpaca-installer-version 0.10) +(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) +(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) +(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) +(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" + :ref nil :depth 1 :inherit ignore + :files (:defaults "elpaca-test.el" (:exclude "extensions")) + :build (:not elpaca--activate-package))) +(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) + (build (expand-file-name "elpaca/" elpaca-builds-directory)) + (order (cdr elpaca-order)) + (default-directory repo)) + (add-to-list 'load-path (if (file-exists-p build) build repo)) + (unless (file-exists-p repo) + (make-directory repo t) + (when (< emacs-major-version 28) (require 'subr-x)) + (condition-case-unless-debug err + (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) + ((zerop (apply #'call-process `("git" nil ,buffer t "clone" + ,@(when-let* ((depth (plist-get order :depth))) + (list (format "--depth=%d" depth) "--no-single-branch")) + ,(plist-get order :repo) ,repo)))) + ((zerop (call-process "git" nil buffer t "checkout" + (or (plist-get order :ref) "--")))) + (emacs (concat invocation-directory invocation-name)) + ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" + "--eval" "(byte-recompile-directory \".\" 0 'force)"))) + ((require 'elpaca)) + ((elpaca-generate-autoloads "elpaca" repo))) + (progn (message "%s" (buffer-string)) (kill-buffer buffer)) + (error "%s" (with-current-buffer buffer (buffer-string)))) + ((error) (warn "%s" err) (delete-directory repo 'recursive)))) + (unless (require 'elpaca-autoloads nil t) + (require 'elpaca) + (elpaca-generate-autoloads "elpaca" repo) + (load "./elpaca-autoloads"))) +(add-hook 'after-init-hook #'elpaca-process-queues) +(elpaca `(,@elpaca-order)) + +;; Optional: Install use-package support +(elpaca elpaca-use-package + (elpaca-use-package-mode)) + +(defun rs/get-default-font () + (cond + ((eq system-type 'windows-nt) "Consolas-11") + ((eq system-type 'gnu/linux) "Consolas-11") + ((eq system-type 'darwin) "Consolas-11"))) + +;; Font settings +;; (set-frame-font (font-spec :size 13)) +(set-face-font 'default (rs/get-default-font)) +;; (add-to-list 'default-frame-alist `(font . ,(rs/get-default-font))) +;; (setq-default line-spacing 0.11) diff --git a/home/emacs/default.nix b/home/emacs/default.nix new file mode 100644 index 0000000..d53929d --- /dev/null +++ b/home/emacs/default.nix @@ -0,0 +1,26 @@ +{ + lib, + pkgs, + inputs, + config, + dots, + ... +}: { + options.custom = with lib; { + emacs = { + enable = mkEnableOption "Enable emacs"; + }; + }; + + config = lib.mkIf config.custom.emacs.enable { + home.packages = with pkgs; [ + emacs + ]; + + home.file.".emacs.d" = { + source = "${pkgs.custom.minimal-emacs-d}"; + }; + + xdg.configFile."emacs".source = config.lib.file.mkOutOfStoreSymlink "${dots}/home/emacs/config"; + }; +} diff --git a/home/email/default.nix b/home/email/default.nix index d7b35c3..6acd9af 100644 --- a/home/email/default.nix +++ b/home/email/default.nix @@ -11,6 +11,7 @@ imports = [ ./davmail.nix ./neomutt + ./mailcap.nix ]; options.custom.email = with lib; { diff --git a/home/shell.nix b/home/shell.nix new file mode 100644 index 0000000..23eaf1a --- /dev/null +++ b/home/shell.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + pkgs, + dots, + ... +}: +{ + options.custom = with lib; { + shell = { + packages = mkOption { + type = + with types; + attrsOf (oneOf [ + str + attrs + package + ]); + default = { }; + apply = custom.mkShellPackages; + description = '' + Attrset of shell packages to install and add to pkgs.custom overlay (for compatibility across multiple shells). + Both string and attr values will be passed as arguments to writeShellApplicationCompletions + ''; + example = '' + shell.packages = { + myPackage1 = "echo 'Hello, World!'"; + myPackage2 = { + runtimeInputs = [ pkgs.hello ]; + text = "hello --greeting 'Hi'"; + }; + } + ''; + }; + # additionalSourceFile = mkOption { + # type = + # with types; + # nullOr (oneOf [ + # path + # str + # ]); + # default = "${dots}/src/home/shell/custom.sh"; + # }; + }; + }; + + config = { + # programs.bash = lib.mkIf (config.custom.shell.additionalSourceFile != null) { + # bashrcExtra = lib.mkAfter '' + # source ${config.custom.shell.additionalSourceFile} + # ''; + # }; + # programs.zsh = lib.mkIf (config.custom.shell.additionalSourceFile != null) { + # initExtra = lib.mkAfter '' + # source ${config.custom.shell.additionalSourceFile} + # ''; + # }; + }; +} diff --git a/hosts/default.nix b/hosts/default.nix index bf1272c..0e8f59e 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -30,6 +30,7 @@ let ./${host} ./${host}/hardware.nix ../nixos + ../overlays ({config, ...}: { home-manager = { useGlobalPkgs = true; diff --git a/hosts/lemur/default.nix b/hosts/lemur/default.nix index 9268848..3353ff8 100644 --- a/hosts/lemur/default.nix +++ b/hosts/lemur/default.nix @@ -43,6 +43,7 @@ # home manager hm.custom = { + emacs.enable = true; latex.enable = false; gui = { default.enable = true; diff --git a/hosts/pickwick/default.nix b/hosts/pickwick/default.nix index 8234e31..2bc66f1 100644 --- a/hosts/pickwick/default.nix +++ b/hosts/pickwick/default.nix @@ -45,6 +45,7 @@ # home manager hm.custom = { + emacs.enable = true; latex.enable = true; gui = { default.enable = true; diff --git a/lib.nix b/lib.nix new file mode 100644 index 0000000..b3cf768 --- /dev/null +++ b/lib.nix @@ -0,0 +1,67 @@ +{ + lib, + pkgs, + ... +}: +lib.extend ( + _: libprev: { + custom = rec { + # taken from https://github.com/iynaix/dotfiles/blob/main/lib.nix + # writeShellApplication with support for completions + writeShellApplicationCompletions = + { + name, + bashCompletion ? null, + zshCompletion ? null, + fishCompletion ? null, + ... + }@shellArgs: + let + inherit (pkgs) writeShellApplication writeTextFile symlinkJoin; + # get the needed arguments for writeShellApplication + app = writeShellApplication (lib.intersectAttrs (lib.functionArgs writeShellApplication) shellArgs); + completions = + lib.optional (bashCompletion != null) (writeTextFile { + name = "${name}.bash"; + destination = "/share/bash-completion/completions/${name}.bash"; + text = bashCompletion; + }) + ++ lib.optional (zshCompletion != null) (writeTextFile { + name = "${name}.zsh"; + destination = "/share/zsh/site-functions/_${name}"; + text = zshCompletion; + }) + ++ lib.optional (fishCompletion != null) (writeTextFile { + name = "${name}.fish"; + destination = "/share/fish/vendor_completions.d/${name}.fish"; + text = fishCompletion; + }); + in + if lib.length completions == 0 then + app + else + symlinkJoin { + inherit name; + inherit (app) meta; + paths = [ app ] ++ completions; + }; + + # taken from https://github.com/iynaix/dotfiles/blob/main/lib.nix + # produces an attrset shell package with completions from either a string / writeShellApplication attrset / package + mkShellPackages = lib.mapAttrs ( + name: value: + if lib.isString value then + pkgs.writeShellApplication { + inherit name; + text = value; + } + # packages + else if lib.isDerivation value then + value + # attrs to pass to writeShellApplication + else + writeShellApplicationCompletions (value // { inherit name; }) + ); + }; + } +) diff --git a/nixos/default.nix b/nixos/default.nix index a9ee12b..cc5e96e 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -14,61 +14,79 @@ ./displaymanager.nix ./keyd.nix ./gnupg.nix + ./nix.nix ./sops.nix ]; - nix = let - flakeInputs = lib.filterAttrs (_: lib.isType "flake") inputs; - in { - settings = { - # Enable flakes and new 'nix' command - experimental-features = "nix-command flakes"; - # Opinionated: disable global registry - flake-registry = ""; - # Workaround for https://github.com/NixOS/nix/issues/9574 - nix-path = config.nix.nixPath; + options.custom = with lib; { + shell = { + packages = mkOption { + type = + with types; + attrsOf (oneOf [ + str + attrs + package + ]); + apply = custom.mkShellPackages; + default = { }; + description = '' + Attrset of shell packages to install and add to pkgs.custom overlay (for compatibility across multiple shells). + Both string and attr values will be passed as arguments to writeShellApplicationCompletions + ''; + example = '' + shell.packages = { + myPackage1 = "echo 'Hello, World!'"; + myPackage2 = { + runtimeInputs = [ pkgs.hello ]; + text = "hello --greeting 'Hi'"; + }; + } + ''; + }; }; - # Opinionated: disable channels - channel.enable = false; - - # Opinionated: make flake registry and nix path match flake inputs - registry = lib.mapAttrs (_: flake: {inherit flake;}) flakeInputs; - nixPath = lib.mapAttrsToList (n: _: "${n}=flake:${n}") flakeInputs; - }; - - # networking - networking.hostName = host; - networking.hostId = builtins.substring 0 8 (builtins.hashString "md5" config.networking.hostName); - networking.networkmanager.enable = true; - - programs.dconf.enable = true; - - services.libinput = { - enable = true; - touchpad = { - disableWhileTyping = true; - naturalScrolling = true; + symlinks = mkOption { + type = types.attrsOf types.str; + default = { }; + description = "Symlinks to create in the format { dest = src;}"; }; }; - security.rtkit.enable = true; - security.polkit.enable = true; + config = { + # networking + networking.hostName = host; + networking.hostId = builtins.substring 0 8 (builtins.hashString "md5" config.networking.hostName); + networking.networkmanager.enable = true; - services.openssh = { - enable = true; - settings = { - # Opinionated: forbid root login through SSH. - PermitRootLogin = "no"; - # Opinionated: use keys only. - # Remove if you want to SSH using passwords - PasswordAuthentication = false; + programs.dconf.enable = true; + + services.libinput = { + enable = true; + touchpad = { + disableWhileTyping = true; + naturalScrolling = true; + }; }; + + security.rtkit.enable = true; + security.polkit.enable = true; + + services.openssh = { + enable = true; + settings = { + # Opinionated: forbid root login through SSH. + PermitRootLogin = "no"; + # Opinionated: use keys only. + # Remove if you want to SSH using passwords + PasswordAuthentication = false; + }; + }; + + environment.systemPackages = with pkgs; [ + vim + htop + ] ++ (lib.attrValues config.custom.shell.packages); + + system.stateVersion = "25.05"; }; - - environment.systemPackages = with pkgs; [ - vim - htop - ]; - - system.stateVersion = "25.05"; } diff --git a/nixos/nix.nix b/nixos/nix.nix new file mode 100644 index 0000000..b8981a8 --- /dev/null +++ b/nixos/nix.nix @@ -0,0 +1,138 @@ +{ + config, + host, + inputs, + lib, + pkgs, + self, + user, + dots, + ... +}: + +{ + # execute shebangs that assume hardcoded shell paths + services.envfs.enable = true; + + # run unpatched binaries on nixos + programs.nix-ld.enable = true; + + environment = { + # for nixlang / nixpkgs + systemPackages = with pkgs; [ + nix-init + nix-update + nixfmt-rfc-style + ]; + }; + + # make a symlink of flake within the generation (e.g. /run/current-system/src) + system.extraSystemBuilderCmds = "ln -s ${self.sourceInfo.outPath} $out/src"; + + systemd.tmpfiles.rules = [ + # cleanup nixpkgs-review cache on boot + "D! ${config.hm.xdg.cacheHome}/nixpkgs-review 1755 ${user} users 1d" + # cleanup channels so nix stops complaining + "D! /nix/var/nix/profiles/per-user/root 1755 root root 1d" + ]; + + custom.shell.packages = { + # list all installed packages + nix-list-packages = { + text = + let + allPkgs = map (p: p.name) ( + config.environment.systemPackages ++ config.users.users.${user}.packages ++ config.hm.home.packages + ); + in + ''sort -ui <<< "${lib.concatLines allPkgs}"''; + }; + }; + + nix = + let + flakeInputs = lib.filterAttrs (_: lib.isType "flake") inputs; + nixPath = lib.mapAttrsToList (n: _: "${n}=flake:${n}") flakeInputs; + in + { + inherit nixPath; + channel.enable = false; + # required for nix-shell -p to work + registry = lib.mapAttrs (_: flake: {inherit flake;}) flakeInputs; + gc = { + # Automatic garbage collection + automatic = true; + dates = "daily"; + options = "--delete-older-than 7d"; + }; + package = pkgs.nixVersions.latest; + settings = { + auto-optimise-store = true; # Optimise symlinks + # re-evaluate on every rebuild instead of "cached failure of attribute" error + # eval-cache = false; + # required to be set, for some reason nix.nixPath does not write to nix.conf + nix-path = nixPath; + warn-dirty = false; + # removes ~/.nix-profile and ~/.nix-defexpr + use-xdg-base-directories = true; + + # use flakes + experimental-features = [ + "nix-command" + "flakes" + ]; + substituters = [ + "https://hyprland.cachix.org" + "https://nix-community.cachix.org" + "https://ghostty.cachix.org" + "https://rayandrew.cachix.org" + ]; + # allow building and pushing of laptop config from desktop + trusted-users = [ user ]; + trusted-public-keys = [ + "hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc=" + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + "ghostty.cachix.org-1:QB389yTa6gTyneehvqG58y0WnHjQOqgnA+wBnpWWxns=" + "rayandrew.cachix.org-1:kJnvdWgUyErPGaQWgh/yyu91szgRYD+V/WQ4Dbc4n9M=" + ]; + }; + # // lib.optionalAttrs (config.nix.package.pname == "lix") { + # repl-overlays = [ ./repl-overlays.nix ]; + # }; + }; + + # better nixos generation label + # https://reddit.com/r/NixOS/comments/16t2njf/small_trick_for_people_using_nixos_with_flakes/k2d0sxx/ + system.nixos.label = lib.concatStringsSep "-" ( + (lib.sort (x: y: x < y) config.system.nixos.tags) + ++ [ "${config.system.nixos.version}.${self.sourceInfo.shortRev or "dirty"}" ] + ); + + # add nixos-option workaround for flakes + # https://github.com/NixOS/nixpkgs/issues/97855#issuecomment-1075818028 + nixpkgs.overlays = [ + (_: prev: { + nixos-option = + let + flake-compat = prev.fetchFromGitHub { + owner = "edolstra"; + repo = "flake-compat"; + rev = "12c64ca55c1014cdc1b16ed5a804aa8576601ff2"; + hash = "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko="; + }; + prefix = ''(import ${flake-compat} { src = ${dots}; }).defaultNix.nixosConfigurations.${host}''; + in + prev.runCommandNoCC "nixos-option" { buildInputs = [ prev.makeWrapper ]; } '' + makeWrapper ${lib.getExe prev.nixos-option} $out/bin/nixos-option \ + --add-flags --config_expr \ + --add-flags "\"${prefix}.config\"" \ + --add-flags --options_expr \ + --add-flags "\"${prefix}.options\"" + ''; + }) + ]; + + # enable man-db cache for fish to be able to find manpages + # https://discourse.nixos.org/t/fish-shell-and-manual-page-completion-nixos-home-manager/15661 + documentation.man.generateCaches = true; +} diff --git a/overlays/default.nix b/overlays/default.nix new file mode 100644 index 0000000..6219431 --- /dev/null +++ b/overlays/default.nix @@ -0,0 +1,27 @@ +{ + inputs, + pkgs, + ... +}: +{ + nixpkgs.overlays = [ + (_: prev: { + # include nixpkgs stable + stable = import inputs.nixpkgs-stable { + inherit (prev.pkgs) system; + config.allowUnfree = true; + }; + + # include custom packages + custom = + (prev.custom or { }) + // (import ../packages { + inherit (prev) pkgs; + inherit inputs; + }); + + # use nixfmt-rfc-style as the default + nixfmt = prev.nixfmt-rfc-style; + }) + ]; +} diff --git a/packages/default.nix b/packages/default.nix new file mode 100644 index 0000000..8fcee4e --- /dev/null +++ b/packages/default.nix @@ -0,0 +1,10 @@ +{ + inputs, + pkgs, + ... +}: +let + inherit (pkgs) lib callPackage; +in rec { + minimal-emacs-d = callPackage ./minimal-emacs-d { }; +} diff --git a/packages/minimal-emacs-d/.gitignore b/packages/minimal-emacs-d/.gitignore new file mode 100644 index 0000000..c531d98 --- /dev/null +++ b/packages/minimal-emacs-d/.gitignore @@ -0,0 +1 @@ +*.elc diff --git a/packages/minimal-emacs-d/030825-init-file-debug.patch b/packages/minimal-emacs-d/030825-init-file-debug.patch new file mode 100644 index 0000000..bfb71a2 --- /dev/null +++ b/packages/minimal-emacs-d/030825-init-file-debug.patch @@ -0,0 +1,13 @@ +diff --git a/early-init.el b/early-init.el +index dfea2f0..d60b6ef 100644 +--- a/early-init.el ++++ b/early-init.el +@@ -32,7 +32,7 @@ turned on.") + (defvar minimal-emacs-frame-title-format "%b – Emacs" + "Template for displaying the title bar of visible and iconified frame.") + +-(defvar minimal-emacs-debug (bound-and-true-p init-file-debug) ++(defvar minimal-emacs-debug nil + "Non-nil to enable debug.") + + (defvar minimal-emacs-gc-cons-threshold (* 16 1024 1024) diff --git a/packages/minimal-emacs-d/default.nix b/packages/minimal-emacs-d/default.nix new file mode 100644 index 0000000..9b09ed8 --- /dev/null +++ b/packages/minimal-emacs-d/default.nix @@ -0,0 +1,35 @@ +{ lib, stdenv, pkgs }: + +let + pre-early-init = ./pre-early-init.el; +in +stdenv.mkDerivation { + pname = "minimal-emacs.d"; + version = "02-08-2025"; + + src = pkgs.fetchFromGitHub { + owner = "jamescherti"; + repo = "minimal-emacs.d"; + rev = "e44aa459d5eb5af2f868dc490e4d05efca308915"; + # rev = "08f077545a0f45a1701333406fd1afe8be77a752"; + sha256 = "sha256-ABHv+TUQpBoXkg75iL2ROJoGjT+iUZQHZD9b4Z8Q4kQ="; + }; + + patches = [ + ./030825-init-file-debug.patch + ]; + + installPhase = '' + mkdir $out + cp -r * "$out/" + cp -r ${pre-early-init} "$out/pre-early-init.el" + ''; + + meta = with lib; { + description = "A Customizable Emacs init.el and early-init.el that Provides Better Defaults and Faster Startup"; + homepage = "https://github.com/jamescherti/minimal-emacs.d"; + platforms = platforms.all; + maintainers = [ maintainers.rayandrew ]; + license = licenses.gpl3; + }; +} diff --git a/packages/minimal-emacs-d/pre-early-init.el b/packages/minimal-emacs-d/pre-early-init.el new file mode 100644 index 0000000..938e5fa --- /dev/null +++ b/packages/minimal-emacs-d/pre-early-init.el @@ -0,0 +1,13 @@ +;;; pre-early-init.el --- Pre Early Initalization -*- no-byte-compile: t; lexical-binding: t; -*- + +(setq minimal-emacs-user-directory (expand-file-name "~/.config/emacs")) +(minimal-emacs-load-user-init "pre-early-init.el") + +(defun display-startup-time () + "Display the startup time and number of garbage collections." + (message "Emacs loaded in %.2f seconds (Init only: %.2fs) with %d garbage collections." + (time-to-seconds (time-since before-init-time)) + (float-time (time-subtract after-init-time before-init-time)) + gcs-done)) + +(add-hook 'emacs-startup-hook #'display-startup-time 100)