/nixpkgs

My Nix and Emacs setup using org-babel.

Nix and Emacs configuration

Contents of this file get tangled to $HOME via

(org-babel-tangle)

This exports configuration files to $HOME/.config/nixpkgs/. The Nix user environment gets populated by running

nix-env -iAr nixpkgs.tom

Setting up a new box

This is a checklist for reinstalling my laptop.

  1. Install Ubuntu.
  2. Install tools outside of Nix: e.g., dropbox, docker, nix.
  3. Symlink $HOME/.gnupg and $HOME/.profile.
  4. Tangle this file using a temporary nix-shell environment.

Nix configuration

Most of my software gets installed via Nix. First, proprietary software is enabled in config.nix:

{ allowUnfree = true; }

Next, I define a meta-package depending on all the necessary packages:

self: super: {
  tom = super.tom or {} // {
    emacsGit = self.lowPrio self.emacsGit; # emacs also writes .desktop => set lower priority
    emacs-custom-desktop = self.emacs-custom-desktop;
    nix = self.nix;
    conda = self.conda;
    slack = self.slack;
    gnupg = self.gnupg;
    pass = self.pass;
    ripgrep = self.ripgrep;
    htop = self.htop;
    gmsh = self.gmsh;
    suitesparse = self.suitesparse;
    gnumake = self.gnumake;
    xclip = self.xclip;
    graphviz = self.graphviz;
    git = self.git;
    vim = self.vim;
    tmux = self.tmux;
    pandoc = self.pandoc;
  };
}

Emacs overlays

I’m using the bleeding edge Emacs overlay from https://github.com/nix-community/emacs-overlay, but fixing it to a specific commit so package versions are equivalent on all computers.

import (builtins.fetchTarball {
      url = "https://github.com/nix-community/emacs-overlay/archive/86707a04d9679a92b7454e073a13e0c676e59e6d.tar.gz";
      sha256 = "1x3l0iypc1zchm364axp66m70jjik4pl413lrwj37w320gv23fh5";
})

The following overlay enables a custom Emacs .desktop file (below) which fixes dead keys (^, `, etc.) in Emacs 27/Ubuntu 19.10. It also uses the function emacsWithPackagesFromUsePackage from the above Emacs overlay to install a set of (M)ELPA packages.

self: super: {
  emacs-custom-desktop = with self; stdenv.mkDerivation {
    name = "emacs-custom-desktop";
    src = ./emacs;
    buildInputs = [];
    installPhase = ''
      mkdir -p $out/share/applications
      cp emacs.desktop $out/share/applications
    '';
  };
  emacsGit = (self.emacsWithPackagesFromUsePackage {
      config = builtins.readFile ~/.config/emacs/init.el;
      package = super.emacsGit;
  });
}

This is the custom .desktop file. It simply unsets XMODIFIERS which fixes the dead keys.

[Desktop Entry]
Name=Emacs
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=env XMODIFIERS="" emacs %F
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs
Keywords=Text;Editor;

Emacs configuration

The Emacs configuration comprises the rest of the snippets.

(require 'package)
(package-initialize 'noactivate)
(eval-when-compile
  (require 'use-package))

Org-mode

examples/org.png

(use-package org
  :commands org-babel-do-load-languages
  :config
  (unbind-key "C-," org-mode-map)
  (unbind-key "C-." org-mode-map)
  :init
  (add-hook 'org-mode-hook (lambda ()
                             (fset 'tex-font-lock-suscript 'ignore)
                             (org-babel-do-load-languages
                              'org-babel-load-
                              '((python . t)
                                (shell . t)))))
  (add-hook 'org-agenda-finalize-hook
            (lambda ()
              (save-excursion
                (set-face-attribute 'org-agenda-structure nil :height (car moe-theme-resize-org-title))
                (set-face-attribute 'org-agenda-date nil :height (cadr moe-theme-resize-org-title))
                (set-face-attribute 'org-agenda-date-today nil :height (cadr moe-theme-resize-org-title))
                (set-face-attribute 'org-agenda-date-weekend nil :height (cadr moe-theme-resize-org-title))
                (color-org-header "inbox:" "#DDDDFF" "black")
                (color-org-header "work:" "#FFDDDD" "red")
                (color-org-header "research:" "#DDFFDD" "DarkGreen"))))
  (defun color-org-header (tag backcolor forecolor)
    ""
    (interactive)
    (goto-char (point-min))
    (while (re-search-forward tag nil t)
      (add-text-properties
       (match-beginning 0) (+ (match-beginning 0) 10)
       `(face (:background, backcolor, :foreground, forecolor)))))
  (fset 'tex-font-lock-suscript 'ignore)
  (defun capture-report-date-file ()
    (interactive)
    (let ((name (read-string "Name: ")))
      (expand-file-name (format "%s-%s.org"
                                (format-time-string "%Y-%m-%d")
                                name)
                        "~/Dropbox/Notes/")))
  (setq org-default-notes-file "~/Dropbox/Notes/gtd/inbox.org"
        org-agenda-files '("~/Dropbox/Notes/gtd/inbox.org"
                           "~/Dropbox/Notes/gtd/tickler.org"
                           "~/Dropbox/Notes/gtd/research.org"
                           "~/Dropbox/Notes/gtd/work.org")
        org-refile-targets '(("~/Dropbox/Notes/gtd/inbox.org" . (:maxlevel . 1))
                             ("~/Dropbox/Notes/gtd/tickler.org" . (:maxlevel . 1))
                             ("~/Dropbox/Notes/gtd/research.org" . (:maxlevel . 1))
                             ("~/Dropbox/Notes/gtd/work.org" . (:maxlevel . 1)))
        org-log-done 'time
        org-tags-column 0
        org-export-babel-evaluate nil
        org-startup-folded nil
        org-adapt-indentation nil
        org-refile-use-outline-path 'file
        org-structure-template-alist '(("l" . "latex latex")
                                       ("s" . "src"))
        org-outline-path-complete-in-steps nil
        org-duration-format '(("d" . nil) ("h" . t) (special . 2))
        org-format-latex-options '(:foreground default
                                   :background default
                                   :scale 1.5
                                   :html-foreground "Black"
                                   :html-background "Transparent"
                                   :html-scale 1.0
                                   :matchers
                                   ("begin" "$1" "$" "$$" "\\(" "\\["))
        org-src-preserve-indentation t
        org-confirm-babel-evaluate nil
        org-html-validation-link nil
        python-shell-completion-native-disabled-interpreters '("python")
        org-babel-default-header-args:sh '((:prologue . "exec 2>&1")
                                           (:epilogue . ":"))
        org-capture-templates '(("t" "Todo" entry
                                 (file "~/Dropbox/Notes/gtd/inbox.org")
                                 "* TODO %?\n  SCHEDULED: %t\n%i\n%a")
                                ("k" "Entry" entry
                                 (file "~/Dropbox/Notes/gtd/inbox.org")
                                 "* %?\n%t")
                                ("n" "Note" entry
                                 (file capture-report-date-file))))
  :bind (("C-c c" . org-capture)
         ("C-c a" . org-agenda)))

Find, select and modify: ivy/counsel/swiper and wgrep

examples/ivy.png

(use-package ivy
  :commands
  ivy-mode
  :init
  (ivy-mode 1)
  (setq ivy-height 15
        ivy-fixed-height-minibuffer t
       	ivy-use-virtual-buffers t)
  :bind (("C-x b" . ivy-switch-buffer)
         ("C-c r" . ivy-resume)
	 ("C-x C-b" . ibuffer)))

(use-package counsel
  :init
  (setq counsel-find-file-ignore-regexp "\\archive\\'")
  (defun counsel-org-rg ()
    "Search org notes using ripgrep."
    (interactive)
    (counsel-rg "-g*org -g!*archive* -- " "~/Dropbox/Notes" nil nil))
  (defun counsel-nixpkgs-rg ()
    "Search nixpkgs using ripgrep."
    (interactive)
    (counsel-rg "" "~/.nix-defexpr/channels/nixpkgs" nil nil))
  (defun counsel-nixpkgs-file ()
    "Search nixpkgs using ripgrep."
    (interactive)
    (counsel-file-jump "" "~/.nix-defexpr/channels/nixpkgs"))
  :bind (("M-x" . counsel-M-x)
         ("C-x C-f" . counsel-find-file)
         ("C-c g" . counsel-rg)
         ("C-c G" . counsel-git)
         ("C-c o" . counsel-org-rg)
         ("C-c l" . counsel-nixpkgs-rg)
         ("C-c L" . counsel-nixpkgs-file)
         ("C-x b" . counsel-switch-buffer)
         ("C-c h" . counsel-minibuffer-history)
         ("M-y" . counsel-yank-pop)))

(use-package swiper
  :bind ("C-c s" . swiper))

(use-package wgrep)

Magit

(use-package magit
  :init
  (setq magit-repository-directories '(("~/src" . 1)))
  :bind (("C-x g" . magit-status)
         ("C-c M-g" . magit-file-dispatch)))

Multiline editing

This triplet of packages allows doing magical things.

(use-package expand-region
  :after (org)
  :bind ("C-." . er/expand-region)
  :init
  (require 'expand-region)
  (require 'cl)
  (defun mark-around* (search-forward-char)
    (let* ((expand-region-fast-keys-enabled nil)
           (char (or search-forward-char
                     (char-to-string
                      (read-char "Mark inner, starting with:"))))
           (q-char (regexp-quote char))
           (starting-point (point)))
      (when search-forward-char
        (search-forward char (point-at-eol)))
      (cl-flet ((message (&rest args) nil))
        (er--expand-region-1)
        (er--expand-region-1)
        (while (and (not (= (point) (point-min)))
                    (not (looking-at q-char)))
          (er--expand-region-1))
        (er/expand-region -1))))
  (defun mark-around ()
    (interactive)
    (mark-around* nil))
  (define-key global-map (kbd "M-i") 'mark-around))

(use-package multiple-cursors
  :init
  (define-key global-map (kbd "C-'") 'mc-hide-unmatched-lines-mode)
  (define-key global-map (kbd "C-,") 'mc/mark-next-like-this)
  (define-key global-map (kbd "C-;") 'mc/mark-all-dwim))

(use-package phi-search
  :after multiple-cursors
  :init (require 'phi-replace)
  :bind ("C-:" . phi-replace)
  :bind (:map mc/keymap
              ("C-s" . phi-search)
              ("C-r" . phi-search-backward)))

Customizations to dired

examples/dired.png

(defalias 'use-internal-package 'use-package)

(use-internal-package term)

(use-internal-package dired-x)

(use-internal-package dired
  :after (term dired-x)
  :init
  (setq dired-dwim-target t)
  (setq dired-omit-files "^\\...+$")
  (defun run-gnome-terminal-here ()
    (interactive)
    (shell-command "gnome-terminal"))
  (setq dired-guess-shell-alist-user
        '(("\\.pdf\\'" "evince")
          ("\\.eps\\'" "evince")
          ("\\.jpe?g\\'" "eog")
          ("\\.png\\'" "eog")
          ("\\.gif\\'" "eog")
          ("\\.xpm\\'" "eog")))
  :bind (("C-x C-j" . dired-jump))
  :bind (:map dired-mode-map
              ("'" . run-gnome-terminal-here)
              ("j" . swiper)
              ("s" . swiper)))

(use-package dired-k
  :after (dired)
  :bind (:map dired-mode-map
              ("g" . dired-k)))

(use-package diredfl
  :commands diredfl-global-mode
  :init (diredfl-global-mode))

Additional syntax highlighting

(use-package json-mode)

(use-package julia-mode)

(use-package highlight-indentation
  :init (add-hook 'prog-mode-hook 'highlight-indentation-mode))

(use-package yaml-mode)

(use-package csv-mode
  :mode "\\.csv$"
  :init (setq csv-separators '(";")))

(use-package markdown-mode
  :commands (markdown-mode)
  :mode (("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode)))

(use-package nix-mode)

Emacs theme

(use-package moe-theme
  :commands moe-light
  :init
  (require 'org)
  (setq moe-theme-resize-markdown-title '(2.0 1.7 1.5 1.3 1.0 1.0))
  (setq moe-theme-resize-org-title '(2.2 1.8 1.6 1.4 1.2 1.0 1.0 1.0 1.0))
  (setq moe-theme-resize-rst-title '(2.0 1.7 1.5 1.3 1.1 1.0))
  (put 'diredp-tagged-autofile-name 'face-alias 'diredfl-tagged-autofile-name)
  (put 'diredp-autofile-name 'face-alias 'diredfl-autofile-name)
  (put 'diredp-ignored-file-name 'face-alias 'diredfl-ignored-file-name)
  (put 'diredp-symlink 'face-alias 'diredfl-symlink)
  (put 'diredp-compressed-file-name 'face-alias 'diredfl-compressed-file-name)
  (put 'diredp-file-suffix 'face-alias 'diredfl-file-suffix)
  (put 'diredp-compressed-extensions 'face-alias 'diredfl-compressed-extensions)
  (put 'diredp-deletion 'face-alias 'diredfl-deletion)
  (put 'diredp-deletion-file-name 'face-alias 'diredfl-deletion-file-name)
  (put 'diredp-flag-mark-line 'face-alias 'diredfl-flag-mark-line)
  (put 'diredp-rare-priv 'face-alias 'diredfl-rare-priv)
  (put 'diredp-number 'face-alias 'diredfl-number)
  (put 'diredp-exec-priv 'face-alias 'diredfl-exec-priv)
  (put 'diredp-file-name 'face-alias 'diredfl-file-name)
  (put 'diredp-dir-heading 'face-alias 'diredfl-dir-heading)
  (put 'diredp-compressed-file-suffix 'face-alias 'diredfl-compressed-file-suffix)
  (put 'diredp-flag-mark 'face-alias 'diredfl-flag-mark)
  (put 'diredp-mode-set-explicitly 'face-alias 'diredfl-mode-set-explicitly)
  (put 'diredp-executable-tag 'face-alias 'diredfl-executable-tag)
  (put 'diredp-global-mode-hook 'face-alias 'diredfl-global-mode-hook)
  (put 'diredp-ignore-compressed-flag 'face-alias 'diredfl-ignore-compressed-flag)
  (put 'diredp-dir-priv 'face-alias 'diredfl-dir-priv)
  (put 'diredp-date-time 'face-alias 'diredfl-date-time)
  (put 'diredp-other-priv 'face-alias 'diredfl-other-priv)
  (put 'diredp-no-priv 'face-alias 'diredfl-no-priv)
  (put 'diredp-link-priv 'face-alias 'diredfl-link-priv)
  (put 'diredp-write-priv 'face-alias 'diredfl-write-priv)
  (put 'diredp-global-mode-buffers 'face-alias 'diredfl-global-mode-buffers)
  (put 'dired-directory 'face-alias 'diredfl-dir-name)
  (put 'diredp-read-priv 'face-alias 'diredfl-read-priv)
  (global-hl-line-mode)
  (moe-light)
  (set-face-attribute 'font-lock-type-face nil :box 1)
  (set-face-attribute 'font-lock-function-name-face nil :box 1))

Python development

My current Python workflow is fairly old school and it’s complemented by counsel-rg and counsel-git.

(use-package dumb-jump
  :bind (("M-." . dumb-jump-go)
         ("M-," . dumb-jump-back))
  :config (setq dumb-jump-selector 'ivy))

(use-package virtualenvwrapper
  :init (setq venv-location "~/.conda/envs"))

(use-package python-pytest
  :bind ("C-c t" . python-pytest-popup))

(use-package hydra)

;; from move-lines package, https://github.com/targzeta/move-lines
(defun move-lines--internal (n)
  "Moves the current line or, if region is actives, the lines surrounding
region, of N lines. Down if N is positive, up if is negative"
  (let* (text-start
         text-end
         (region-start (point))
         (region-end region-start)
         swap-point-mark
         delete-latest-newline)

    (when (region-active-p)
      (if (> (point) (mark))
          (setq region-start (mark))
        (exchange-point-and-mark)
        (setq swap-point-mark t
              region-end (point))))

    (end-of-line)
    (if (< (point) (point-max))
        (forward-char 1)
      (setq delete-latest-newline t)
      (insert-char ?\n))
    (setq text-end (point)
          region-end (- region-end text-end))

    (goto-char region-start)
    (beginning-of-line)
    (setq text-start (point)
          region-start (- region-start text-end))

    (let ((text (delete-and-extract-region text-start text-end)))
      (forward-line n)
      (when (not (= (current-column) 0))
        (insert-char ?\n)
        (setq delete-latest-newline t))
      (insert text))

    (forward-char region-end)

    (when delete-latest-newline
      (save-excursion
        (goto-char (point-max))
        (delete-char -1)))

    (when (region-active-p)
      (setq deactivate-mark nil)
      (set-mark (+ (point) (- region-start region-end)))
      (if swap-point-mark
          (exchange-point-and-mark)))))

(defun move-lines-up (n)
  "Moves the current line or, if region is actives, the lines surrounding
region, up by N lines, or 1 line if N is nil."
  (interactive "p")
  (if (eq n nil)
      (setq n 1))
  (move-lines--internal (- n)))

(defun move-lines-down (n)
  "Moves the current line or, if region is actives, the lines surrounding
region, down by N lines, or 1 line if N is nil."
  (interactive "p")
  (if (eq n nil)
      (setq n 1))
  (move-lines--internal n))

(defun tom/shift-left (start end &optional count)
  "Shift region left and activate hydra."
  (interactive
   (if mark-active
       (list (region-beginning) (region-end) current-prefix-arg)
     (list (line-beginning-position) (line-end-position) current-prefix-arg)))
  (python-indent-shift-left start end count)
  (tom/hydra-move-lines/body))

(defun tom/shift-right (start end &optional count)
  "Shift region right and activate hydra."
  (interactive
   (if mark-active
       (list (region-beginning) (region-end) current-prefix-arg)
     (list (line-beginning-position) (line-end-position) current-prefix-arg)))
  (python-indent-shift-right start end count)
  (tom/hydra-move-lines/body))

(defun tom/move-lines-p ()
  "Move lines up once and activate hydra."
  (interactive)
  (move-lines-up 1)
  (tom/hydra-move-lines/body))

(defun tom/move-lines-n ()
  "Move lines down once and activate hydra."
  (interactive)
  (move-lines-down 1)
  (tom/hydra-move-lines/body))

(defhydra tom/hydra-move-lines ()
  "Move one or multiple lines"
  ("n" move-lines-down "down")
  ("p" move-lines-up "up")
  ("<" python-indent-shift-left "left")
  (">" python-indent-shift-right "right"))

(define-key global-map (kbd "C-c n") 'tom/move-lines-n)
(define-key global-map (kbd "C-c p") 'tom/move-lines-p)
(define-key global-map (kbd "C-c <") 'tom/shift-left)
(define-key global-map (kbd "C-c >") 'tom/shift-right)

Miscellanous stuff

Including things that I yet haven’t categorized.

(use-package exec-path-from-shell
  :commands exec-path-from-shell-initialize
  :init (exec-path-from-shell-initialize))

(use-package transient)

(use-package docker
  :bind ("C-c d" . docker))

(use-package restclient)

(use-package ob-restclient
  :after (org restclient)
  :init (org-babel-do-load-languages
         'org-babel-load-languages '((restclient . t))))

(use-package htmlize)

(use-package org-ref)

(use-package which-key
  :commands which-key-mode
  :init (which-key-mode))

(use-package ivy-pass
  :commands ivy-pass
  :init
  (defun pass ()
    "Call ivy-pass."
    (interactive)
    (ivy-pass)))

;; useful functions

(defun tom/unfill-paragraph (&optional region)
  "Take REGION and turn it into a single line of text."
  (interactive (progn (barf-if-buffer-read-only) '(t)))
  (let ((fill-column (point-max))
        (emacs-lisp-docstring-fill-column t))
    (fill-paragraph nil region)))

(define-key global-map "\M-Q" 'tom/unfill-paragraph)

(defun tom/increment-number-decimal (&optional arg)
  "Increment the number forward from point by 'arg'."
  (interactive "p*")
  (save-excursion
    (save-match-data
      (let (inc-by field-width answer)
        (setq inc-by (if arg arg 1))
        (skip-chars-backward "0123456789")
        (when (re-search-forward "[0-9]+" nil t)
          (setq field-width (- (match-end 0) (match-beginning 0)))
          (setq answer (+ (string-to-number (match-string 0) 10) inc-by))
          (when (< answer 0)
            (setq answer (+ (expt 10 field-width) answer)))
          (replace-match (format (concat "%0" (int-to-string field-width) "d")
                                 answer)))))))

(global-set-key (kbd "C-c x") 'tom/increment-number-decimal)

;; other global configurations

;; show current function in modeline
(which-function-mode)

;; scroll screen
(define-key global-map "\M-n" 'scroll-up-line)
(define-key global-map "\M-p" 'scroll-down-line)

;; change yes/no to y/n
(defalias 'yes-or-no-p 'y-or-n-p)
(setq confirm-kill-emacs 'yes-or-no-p)

;; enable winner-mode, previous window config with C-left
(winner-mode 1)

;; windmove
(windmove-default-keybindings)

;; fonts
(set-face-attribute 'mode-line-inactive nil :font "Ubuntu Mono-12")
(set-face-attribute 'default nil :font "Ubuntu Mono-16")
(set-face-attribute 'mode-line nil :font "Ubuntu Mono-12")

;; disable tool and menu bars
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(blink-cursor-mode -1)

;; change gc behavior
(setq gc-cons-threshold 50000000)

;; warn when opening large file
(setq large-file-warning-threshold 100000000)

;; disable startup screen
(setq inhibit-startup-screen t)

;; useful frame title format
(setq frame-title-format
      '((:eval (if (buffer-file-name)
                   (abbreviate-file-name (buffer-file-name))
                 "%b"))))

;; automatic revert
(global-auto-revert-mode t)

;; highlight parenthesis, easier jumping with C-M-n/p
(show-paren-mode 1)
(setq show-paren-style 'expression)
(setq show-paren-delay 0)

;; control indentation
(setq-default indent-tabs-mode nil)
(setq tab-width 4)
(setq c-basic-offset 4)

;; modify scroll settings
(setq scroll-preserve-screen-position t)

;; set default fill width (e.g. M-q)
(setq-default fill-column 80)

;; window dividers
(fringe-mode 0)
(setq window-divider-default-places t
      window-divider-default-bottom-width 1
      window-divider-default-right-width 1)
(window-divider-mode 1)

;; display time in modeline
(display-time-mode 1)

;; put all backups to same directory to not clutter directories
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))

;; display line numbers
(global-display-line-numbers-mode)

;; browse in chrome
(setq browse-url-browser-function 'browse-url-chrome)

;; don't fontify latex
(setq font-latex-fontify-script nil)

;; set default encodings to utf-8
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-language-environment 'utf-8)
(set-selection-coding-system 'utf-8)

;; make Customize to not modify this file
(setq custom-file (make-temp-file "emacs-custom"))

;; enable all disabled commands
(setq disabled-command-function nil)

;; ediff setup
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

;; unbind keys
(unbind-key "C-z" global-map)

;; change emacs frame by number
(defun tom/select-frame (n)
  "Select frame identified by the number N."
  (interactive)
  (let ((frame (nth n (reverse (frame-list)))))
    (if frame
        (select-frame-set-input-focus frame)
      (select-frame-set-input-focus (make-frame))
      (toggle-frame-fullscreen))))

(define-key global-map
  (kbd "M-1")
  (lambda () (interactive)
    (tom/select-frame 0)))
(define-key global-map
  (kbd "M-2")
  (lambda () (interactive)
    (tom/select-frame 1)))
(define-key global-map
  (kbd "M-3")
  (lambda () (interactive)
    (tom/select-frame 2)))
(define-key global-map
  (kbd "M-4")
  (lambda () (interactive)
    (tom/select-frame 3)))

;; bind find config
(define-key global-map (kbd "<f10>")
  (lambda () (interactive)
    (find-file "~/Dropbox/Config/nixpkgs/README.org")))

;; bind compile
(define-key global-map (kbd "<f12>") 'compile)

;; load private configurations
(load "~/Dropbox/Config/emacs/private.el" t)