Dotfilesยถ

Some of my dotfiles

bash
00-options

shopt -s autocd         # If no command found, but matches a directory, cd into it
shopt -s checkjobs      # Warn about background jobs before exiting
shopt -s checkwinsize   # Update the COLUMNS and LINES environment variables between each command
shopt -s extglob        # Enable extended pattern matching features
shopt -s globstar       # Enable recursive globbing i.e `./**/*.py`

[ -d "$HOME/Projects" ] && export CDPATH=".:~/Projects"

paths=(
    "$HOME/go/bin"
    "$HOME/.cargo/bin"
    "$HOME/.npm-packages/bin"
)

for p in ${paths[@]}
do
    [ -d $p ] && export PATH="$p:$PATH"
done

shopt -s histappend

HISTCONTROL=erasedups
HISTFILESIZE=100000
HISTIGNORE='cd:ls'
HISTSIZE=10000
10-aliases

alias ls='ls -CFhX --color=auto --group-directories-first'
alias pypath='echo $PYTHONPATH | tr '\'':'\'' '\''\n'\'''
20-prompt

__venv_py_version()
{
    if [ -z "${VIRTUAL_ENV}" ]; then
        echo ""
    else
        echo " ๐Ÿ v$(python --version | sed 's/Python //')"
    fi
}

__is_toolbox()
{
    if [ -f /run/.containerenv ] && [ -f /run/.toolboxenv ]; then
        name=$(grep name /run/.containerenv | sed 's/.*"\(.*\)"/\1/')

    else
        name="\h"
    fi

    echo "\[\e[32m\]${name}\[\e[0m\]"
}

if [ -f "/usr/share/git-core/contrib/completion/git-prompt.sh" ]; then
    source "/usr/share/git-core/contrib/completion/git-prompt.sh"

    export GIT_PS1_SHOWDIRTYSTATE=1       # (*) unstaged changes, (+) staged changes
    export GIT_PS1_SHOWSTASHSTATE=1       # ($) stashed
    export GIT_PS1_SHOWUNTRACKEDFILES=1   # (%) untracked files
    export GIT_PS1_SHOWUPSTREAM=verbose
    export GIT_PS1_SHOWCOLORHINTS=1

    export PROMPT_COMMAND='__git_ps1 "\n\w " "$(__venv_py_version)\n$(__is_toolbox) > " "[๎‚  %s]"'
else
    export PS1="\W\n> "
fi
emacs
lisp
alc-git.el

(defun alc-git-permalink-to-kill-ring ()
  "Construct a permalink URL for the current line or region and place it on
the kill-ring"
  (interactive)
  (if-let* ((repo (alc-git-get-remote-url))
            (commit (alc-git-get-commit))
            (filepath (alc-git-get-filepath))
            (pos (alc-git-get-position))
            (path-start (format "%s#L%s" filepath (nth 0 pos)))
            (url (s-join "/" `(,repo "blob" ,commit ,path-start))))
      (kill-new
       (if (nth 1 pos)
           (format "%s-L%s" url (nth 1 pos))
         url))
    (message "No permalink found")))

(defun alc-git-get-remote-url ()
  "Return the url of the remote associated with the current buffer.

Prioritizes 'upstream' but will fall back to 'origin' if it is unavailable."
  (condition-case err
      (vc-git-repository-url buffer-file-name "upstream")
    (error err
           (condition-case err
               (vc-git-repository-url buffer-file-name "origin")
             (error err nil)))))

(defun alc-git-get-commit ()
  "Return the commit hash for the current file"
  (vc-working-revision buffer-file-truename))

(defun alc-git-get-filepath ()
  "Return the filepath of the current buffer relative to the root of the
repository"
  (string-remove-prefix (vc-git-root buffer-file-truename)
                        buffer-file-truename))

(defun alc-git-get-position ()
  "Return the start and end line number"
  (if (region-active-p)
      `(,(line-number-at-pos (region-beginning))
        ,(line-number-at-pos (region-end)))
    `(,(line-number-at-pos) nil)))

(provide 'alc-git)
alc-jj.el

(defun eshell/jj-diff (&rest args)
  "Eshell wrapper around 'jj diff'"
  (let ((current-dir default-directory))
    (with-current-buffer (get-buffer-create "*jj-diff*")
      ;; Ensure the command uses the current directory from where the command was invoked.
      (setq default-directory current-dir)

      ;; Clear any previous content
      (read-only-mode -1)
      (erase-buffer)

      ;; Call jj, insert output into current buffer
      (call-process "jj" nil t nil "diff" "--no-pager" "--color" "never")
      (diff-mode)

      ;; Re-enable read-only mode and show the buffer
      (read-only-mode)
      (pop-to-buffer (current-buffer)))))

(provide 'alc-jj)
alc-modeline.el

;;; alc-modeline.el --- Modeline configuration -*- lexical-binding: t -*-
;;; Code:

(defgroup alc-modeline nil
  "My custom modeline"
  :group 'mode-line)

(defgroup alc-modeline-faces nil
  "Faces for my custom modeline"
  :group 'alc-modeline)

(defface alc-modeline-project-id-face
  '((default :inherit (bold)))
  "Face for styling the project indicator"
  :group 'alc-modeline-faces)

(defvar-local alc-modeline-project-identification
 '(:eval
   (if-let ((pr (project-current))
            (file (buffer-file-name)))
       (propertize (format "๐Ÿ–ฟ %s " (project-name pr))
                   'face 'alc-modeline-project-id-face))))
(put 'alc-modeline-project-identification 'risky-local-variable t)

(defvar-local alc-modeline-remote-indication
    '(:eval
       (when (file-remote-p default-directory)
         (propertize " โ˜ "
                     'face '(bold)))))
(put 'alc-modeline-remote-indication 'risky-local-variable t)

(defun alc-modeline-buffer-identification-face ()
  "Return the face(s) to apply to the buffer name in the modeline."
  (cond ((and (buffer-file-name)
              (buffer-modified-p))
         'error)
        (buffer-read-only '(italic mode-line-buffer-id))
        (t 'mode-line-buffer-id)))

(defvar-local alc-modeline-buffer-identification
    '(:eval
       (propertize "%b"
                   'face (alc-modeline-buffer-identification-face))))
(put 'alc-modeline-buffer-identification 'risky-local-variable t)

(defun alc-modeline-buffer-position-face ()
  "Return the face(s) to apply to the buffer position in the modeline."
  (if (mode-line-window-selected-p)
      'mode-line
    'mode-line-inactive))

(defvar-local alc-modeline-buffer-position
    '(:eval
      (propertize "%l:%c"
                  'face (alc-modeline-buffer-position-face))))
(put 'alc-modeline-buffer-position 'risky-local-variable t)

(defface alc-modeline-window-dedicated-face
  '((default :inherit (bold)))
  "Face for styling the dedicated window indicator"
  :group 'alc-modeline-faces)

(defvar-local alc-modeline-window-dedicated
    '(:eval
      (when (window-dedicated-p)
        (propertize "๐Ÿ–ˆ "
                    'face 'alc-modeline-window-dedicated-face))))
(put 'alc-modeline-window-dedicated 'risky-local-variable t)

(with-eval-after-load 'modus-themes
  (defun alc-modeline-apply-modus-colors ()
    "Style the modeline using colors provided by the `modus-themes'"
    (if (modus-themes--current-theme) ; Only if a modus-theme is active.
        (modus-themes-with-colors
          (set-face-attribute 'mode-line nil
                              :background bg-active
                              :overline bg-active
                              :box `(:line-width 1 :color ,bg-active :style nil))
          (set-face-attribute 'mode-line-inactive nil
                              :background bg-inactive
                              :overline bg-inactive
                              :box `(:line-width 1 :color ,bg-inactive :style nil))
          (set-face-attribute 'alc-modeline-project-id-face nil
                              :background 'unspecified
                              :foreground blue))))

  (alc-modeline-apply-modus-colors)
  (add-hook 'modus-themes-after-load-theme-hook #'alc-modeline-apply-modus-colors))

(provide 'alc-modeline)
alc-python.el

;;; alc-python.el --- Python configuration -*- lexical-binding: t -*-
;;; Code:

(defun alc-python-env-select ()
  "Select from a list of available Python environments, return the path to
the Python executable"
  (if-let ((prj (project-current)))
      (cond ((alc-python-project-poetry-p prj)
             (alc-python-env-poetry-select prj))
            ((alc-python-project-hatch-p prj)
             (alc-python-env-hatch-select prj)))))

(defun alc-python-env-activate ()
  "Select a Python environment and activate it."
  (interactive)
  (if-let ((selected-env (alc-python-env-select))
           (default-directory (project-root (project-current))))
      (progn
        (setq-local python-shell-interpreter selected-env)
        (modify-dir-local-variable 'python-mode
                                   'python-shell-interpreter
                                   selected-env
                                   'add-or-replace)
        (modify-dir-local-variable 'python-mode
                                   'eglot-workspace-configuration
                                   `(:python
                                     (:pythonPath ,selected-env)
                                     :python.analysis
                                     (:logLevel "trace"))
                                   'add-or-replace)
        ;; Annoyingly, `modify-dir-local-variable' will leave the
        ;; .dir-locals.el file open in an unsaved buffer
        (save-buffer)
        (kill-buffer))))

(defun alc-python-project-hatch-p (prj)
  "Return t if the given Python PRJ is managed by hatch."
  (let ((root (project-root prj)))
    (or (file-exists-p (concat root "hatch.toml"))
        (not (null (gethash "hatch"
                            (gethash "tool"
                                     (alc-python-load-toml (concat root "pyproject.toml"))
                                     (make-hash-table))))))))

(defun alc-python-env-hatch-discover (prj)
  "Return a list of hatch managed environments available in PRJ"
  (if-let* ((default-directory (project-root prj))
            (hatch-exe (executable-find "hatch"))
            (hatch-cmd (format "%s env show --json" hatch-exe))
            (hatch-envs (json-parse-string
                         (shell-command-to-string hatch-cmd))))
      (let ((envs ()))
        (maphash (lambda (k v) (push k envs))
                 hatch-envs)
        envs)))

(defun alc-python-env-hatch-select (prj)
  "Select a Hatch environment defined by the given PRJ"
  (if-let* ((default-directory (project-root prj))
            (hatch-envs (alc-python-env-hatch-discover prj))
            (selected-env (completing-read "Select env: " hatch-envs))
            (hatch-exe (executable-find "hatch"))
            (hatch-cmd (format "%s -e %s env run python -- -c 'import sys;print(sys.executable)'"
                               hatch-exe selected-env)))
      (car (reverse (split-string
                     (string-trim (shell-command-to-string hatch-cmd))
                     "\n")))))

(defun alc-python-project-poetry-p (prj)
  "Return t if the given Python PRJ is managed by poetry."
  (file-exists-p (concat (project-root prj) "poetry.lock")))

(defun alc-python-env-poetry-discover (prj)
  "Return a list of poetry managed environments available in PRJ"
  (if-let* ((default-directory (project-root prj))
            (poetry-exe (executable-find "poetry"))
            (poetry-cmd (format "%s env list" poetry-exe))
            (poetry-envs (shell-command-to-string poetry-cmd)))
      (seq-filter
       (lambda (s) (not (string= s "(Activated)")))
       (split-string poetry-envs))))

(defun alc-python-env-poetry-select (prj)
  "Select a Poetry environment defined by the given PRJ"
  (if-let* ((default-directory (project-root prj))
            (poetry-envs (alc-python-env-poetry-discover prj))
            (selected-env (completing-read "Select env: " poetry-envs))
            (poetry-exe (executable-find "poetry")))
    (string-trim (shell-command-to-string (format "%s env info --executable" poetry-exe)))))

(defun alc-python-library-file-p (file-name)
  "Determine if the given FILE-NAME is a library file"
  (or  (string-match-p "site-packages/" file-name)
       (string-match-p "typeshed-fallback/" file-name)
       (string-match-p "/usr/lib\\(64\\)?/" file-name)))

(defun alc-python-load-toml (filename)
  "Load the toml in FILENAME as json, utilising the TOML parser in the
  Python standard library"
  (let ((cmd (string-join `("python" "-c"
                            "'import json,pathlib,sys,tomllib;print(json.dumps(tomllib.loads(pathlib.Path(sys.argv[1]).read_text())))'"
                            ,filename)
                          " ")))
    (json-parse-string (shell-command-to-string cmd))))

(provide 'alc-python)
alc-tab-bar.el

(defun alc-tab-bar-tab-name-format-hints (name _tab i)
  (if tab-bar-tab-hints (concat (format "-%d-" i) "") name))

(defun alc-tab-bar-tab-group-format-function (tab i &optional current-p)
  (propertize
   (concat (funcall tab-bar-tab-group-function tab))
   'face (if current-p 'tab-bar-tab-group-current 'tab-bar-tab-group-inactive)))

(defun alc-tab-bar-project-other-tab-command ()
  "Run a project command, displaying resultant buffer in a new tab.

This implemented by hacking together parts of
`project--other-place-prefix' and `other-tab-prefix'.

The only difference between this version and `project-other-tab-command'
is that new tabs are automatically placed in a group named according to
the corresponding project.
"
  (interactive)
  (prefix-command-preserve-state)
  (display-buffer-override-next-command
   (lambda (buffer alist)
     (cons (progn
             (display-buffer-in-tab
              buffer (append alist '((tab-group . alc-tab-bar-buffer-to-project-name)
                                     (inhibit-same-window . nil))))
             (selected-window))
           'tab))
   nil "[other-tab]")
  (message "Display next project command buffer in a new tab...")
  (set-transient-map project-prefix-map))

(defun alc-tab-bar-buffer-to-project-name (buffer alist)
  "Given BUFFER, return the corresponding project name, if any"
  (with-current-buffer buffer
    (if-let ((pr (project-current nil)))
        (format "[%s]" (project-name pr)))))

(display-battery-mode)

(setq display-time-format "%H:%M %d/%m/%y"
      display-time-default-load-average nil)
(display-time-mode)

(provide 'alc-tab-bar)
alc-theme.el

(defvar alc-theme-load-light-theme-function nil
  "The function to call in order to load the configured light theme")

(defvar alc-theme-load-dark-theme-function nil
  "The function to call in order to load the configured dark theme")

(defun alc-theme-load-theme (variant)
  "Load the light or dark theme according to VARIANT."
  (pcase variant
    ('light (funcall alc-theme-load-light-theme-function))
    ('dark (funcall alc-theme-load-dark-theme-function))
    (_ (error "Unknown theme variant: %s" variant))))

(defun alc-theme--system-preference-to-variant (pref)
  "Simple helper function for converting the system PREF into the
corresponding variant."
  (if (eq pref 1) 'dark 'light))

(require 'dbus)
(defun alc-theme-sync-to-system-theme (_frame)
  "Choose a light/dark theme based on the current system preference."
  (let ((pref (caar (dbus-call-method
                     :session
                     "org.freedesktop.portal.Desktop"
                     "/org/freedesktop/portal/desktop"
                     "org.freedesktop.portal.Settings" "Read"
                     "org.freedesktop.appearance" "color-scheme"))))
    (alc-theme-load-theme (alc-theme--system-preference-to-variant pref))
    (alc-theme--register-dbus-listener)))

(defvar alc-theme--dbus-listener nil
  "Variable in which to store the dbus listener.")

(defun alc-theme--register-dbus-listener ()
  "Register (if necessary) a DBus listener to react to changes in the
system's light/dark preference."
  (unless alc-theme--dbus-listener
    (setq alc-theme--dbus-listener
          (dbus-register-signal
           :session
           "org.freedesktop.portal.Desktop"
           "/org/freedesktop/portal/desktop"
           "org.freedesktop.portal.Settings"
           "SettingChanged"
           (lambda (path var val)
             (when (and (string= path "org.freedesktop.appearance")
                        (string= var "color-scheme"))
               (alc-theme-load-theme
                (alc-theme--system-preference-to-variant (car val)))))))))

(provide 'alc-theme)
alc-treesitter.el

(setq treesit-language-source-alist
      '((python "https://github.com/tree-sitter/tree-sitter-python" "v0.20.4")
        (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "v0.20.3" "typescript/src")))

(if (treesit-language-available-p 'python)
  (add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode)))

 (use-package combobulate
  :vc (:url "https://github.com/mickeynp/combobulate" :rev "master")
  :hook ((python-ts-mode . combobulate-mode)
         (typescript-ts-mode . combobulate-mode)))

(provide 'alc-treesitter)
early-init.el

(blink-cursor-mode -1)
(tool-bar-mode -1)

(setq inhibit-x-resources t
      inhibit-startup-message t)

(context-menu-mode t)
init.el

;;; init.el -- Emacs configuration -*- lexical-binding: t -*-
(setq custom-file (locate-user-emacs-file "custom.el"))
(when (file-exists-p custom-file)
 (load custom-file))

(package-initialize)
(add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/"))
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))

(dolist (pkg '(s))
  (unless (package-installed-p pkg)
    (unless package-archive-contents
      (package-refresh-contents))
    (package-install pkg)))

(if (file-exists-p "/home/linuxbrew/.linuxbrew/bin")
    (add-to-list 'exec-path "/home/linuxbrew/.linuxbrew/bin"))

(if (file-exists-p "/home/alex/.local/bin")
    (add-to-list 'exec-path "/home/alex/.local/bin"))

(setenv "PATH" (string-join exec-path ":"))

(recentf-mode t)
(savehist-mode t)
(save-place-mode t)

(setq backup-directory-alist `(("." . ,(expand-file-name "backups" user-emacs-directory))))

(repeat-mode 1)

(setq whitespace-style '(face empty trailing lines-char tab-mark))
(global-whitespace-mode t)

(add-hook 'before-save-hook #'whitespace-cleanup)

(setq tab-always-indent t)
(setq-default indent-tabs-mode nil)

(setq compilation-always-kill t
      compilation-scroll-output t)

(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)

(add-to-list 'display-buffer-alist
             '("\\*compilation\\*"
               (display-buffer-reuse-window
                display-buffer-in-previous-window
                display-buffer-reuse-mode-window
                display-buffer-at-bottom)
               (window-height . 0.25)))

(keymap-global-set "<f5>" 'recompile)

(define-minor-mode recompile-on-save-mode
  "When enabled, run `recompile' after the current buffer is saved"
  :lighter " โญฎ"
  (if recompile-on-save-mode
      (add-hook 'after-save-hook 'recompile nil t)
    (remove-hook 'after-save-hook 'recompile t)))

(use-package alc-treesitter
  :load-path "lisp"
  :if (treesit-available-p))

(dolist (kmap (list minibuffer-local-map completion-in-region-mode-map))
  (keymap-set kmap "C-p" #'minibuffer-previous-completion)
  (keymap-set kmap "C-n" #'minibuffer-next-completion))

(setq completion-auto-help 'visible
      completion-auto-select 'second-tab)

(setq completion-cycle-threshold 3)

(setq completions-detailed t
      completions-format 'one-column
      completions-group t
      completions-header-format (propertize "%s candidates\n" 'face 'shadow)
      completions-max-height 15
      completion-show-help nil)

(use-package orderless
  :ensure t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles basic partial-completion)))))

(use-package consult
  :ensure t
  :custom
  (consult-preview-key "M-.")
  :bind (("C-x b" . consult-buffer)))

(use-package embark-consult
  :after (embark consult)
  :ensure t)

(use-package embark
  :ensure t
  :bind (("C-." . embark-act)
         ("M-." . embark-dwim)))

(use-package dired
 :hook ((dired-mode . dired-hide-details-mode)
        (dired-mode . hl-line-mode))
 :config
 (setq dired-dwim-target t
       dired-maybe-use-globstar t
       dired-isearch-filenames 'dwim
       ;; -A, all files - except '.' & '..'
       ;; -G, omit owning group
       ;; -h, human readable file sizes
       ;; -l, long listing, required for dired.
       dired-listing-switches "-AGhl --group-directories-first --time-style=long-iso"))

(setq project-vc-extra-root-markers '("Cargo.toml"
                                      "package.json"
                                      "pyproject.toml"))

(defun alc-project-try-vc-subproject (orig-fun &rest args)
  "Advice for `project-try-vc'.

When using `project-vc-extra-root-markers' to teach project.el
about subprojects within a monorepo, `project-try-vc'
successfully finds the subject's root but fails to detect the
backend. But by calling `vc-responsible-backend' on the found
root, we can fill in the blanks.

As a result, commands like `project-find-file' now respect the
parent repo's .gitignore file even when being run from within a
subproject."
  (let* ((res (apply orig-fun args))
         (dir (nth 2 res))
         (backend (or (nth 1 res)
                      (ignore-errors (vc-responsible-backend dir)))))
    (if dir
        `(vc ,backend ,dir))))

(advice-add 'project-try-vc :around #'alc-project-try-vc-subproject)

(use-package apheleia
  :ensure t
  :config
  (apheleia-global-mode))

(use-package esbonio
  :vc (:url "https://github.com/swyddfa/esbonio.el" :rev "main")
  :hook ((rst-mode . esbonio-eglot-ensure)))

(use-package typescript-ts-mode
  :mode "\\.ts\\'"
  :ensure t
  :hook ((typescript-ts-mode . eglot-ensure)))

(use-package yaml-mode
  :ensure t)

(use-package gptel
  :ensure t
  :bind (("C-c RET" . gptel-send))
  :config
  (setq gptel-model 'llama3.2:latest)
  (setq gptel-backend (gptel-make-ollama "Ollama"
                       :host "localhost:11434"
                       :stream t
                       :models '(llama3.2:latest))))

(use-package magit
  :ensure t
  :defer
  :custom
  (magit-format-file-function #'magit-format-file-nerd-icons))

(use-package diff-hl
  :ensure t
  :hook ((dired-mode . diff-hl-dired-mode))
  :config
  (global-diff-hl-mode)
  (diff-hl-margin-mode))

(use-package forge
  :ensure t
  :after magit)

(cl-defun auth-source-ghcli-search (&rest spec
                                          &key backend require
                                          type max host user port
                                          &allow-other-keys)
  "Given a property list SPEC, return search matches from the `:backend'.
See `auth-source-search' for details on SPEC."
  ;; just in case, check that the type is correct (null or same as the backend)
  (cl-assert (or (null type) (eq type (oref backend type)))
             t "Invalid GH CLI search: %s %s")

  (when-let* ((hostname (string-remove-prefix "api." host))
              ;; split ghub--ident again
              (ghub_ident (split-string (or user "") "\\^"))
              (username (car ghub_ident))
              (package (cadr ghub_ident))
              (cmd (format "gh auth token --hostname '%s'" hostname))
              (token (when (string= package "forge") (string-trim-right (shell-command-to-string cmd))))
              (retval (list
                       :host hostname
                       :user username
                       :secret token)))
    (auth-source-do-debug  "auth-source-ghcli: return %s as final result (plus hidden password)"
                           (seq-subseq retval 0 -2)) ;; remove password
    (list retval)))

(defvar auth-source-ghcli-backend
  (auth-source-backend
   :source "." ;; not used
   :type 'gh-cli
   :search-function #'auth-source-ghcli-search)
  "Auth-source backend for GH CLI.")

(defun auth-source-ghcli-backend-parse (entry)
  "Create a GH CLI auth-source backend from ENTRY."
  (when (eq entry 'gh-cli)
    (auth-source-backend-parse-parameters entry auth-source-ghcli-backend)))

(if (boundp 'auth-source-backend-parser-functions)
    (add-hook 'auth-source-backend-parser-functions #'auth-source-ghcli-backend-parse)
  (advice-add 'auth-source-backend-parse :before-until #'auth-source-ghcli-backend-parse))

(setq auth-sources '(gh-cli))

(use-package alc-git
  :ensure nil
  :load-path "lisp/"
  :bind ("C-x v w" . alc-git-permalink-to-kill-ring))

(use-package python
  :hook ((python-mode . alc-python-mode-hook)
         (python-ts-mode . alc-python-mode-hook))
  :custom
  (python-shell-dedicated 'project))

(with-eval-after-load 'apheleia
  (setf (alist-get 'python-ts-mode apheleia-mode-alist)
       '(ruff-isort ruff)))

(defun alc-python-mode-hook ()
  "Tweaks and config to run when starting `python-mode'"
  (require 'alc-python)
  (setq-local fill-column 88)

  ;; Files in site-packages/ etc. should be read only by default.
  ;; Also do not start eglot in these locations to cut down on the
  ;; number of server instances.
  ;;
  ;; However, make sure this only runs for buffer's associated with a
  ;; file. Previous versions of this code would set `(with-temp-buffer ...)`
  ;; buffers read-only which breaks functions like `python-shell-send-region'!
  (if-let ((file-name (buffer-file-name)))
    (if (alc-python-library-file-p file-name)
        (read-only-mode)
      (eglot-ensure))))

(use-package eglot
  :bind (("<f2>" . eglot-rename))
  :custom
  (eglot-autoshutdown t)
  (eglot-extend-to-xref t)
  :config
  (advice-add 'eglot--workspace-configuration-plist :around #'alc-eglot-fix-workspace-configuration-scope)

  (add-to-list 'display-buffer-alist
               '("\\*EGLOT workspace configuration\\*"
                 (display-buffer-below-selected)
                 (dedicated . t))))

(require 's)

(defun alc-eglot-fix-workspace-configuration-scope (orig-fun server &optional path)
  "Fix the scopeUri of a workspace/configuration request.

When eglot handles a workspace/configuration request with an
associated scopeUri it uses the `file-name-directory' function to
determine the directory from which to resolve the configuration
values.

This causes an issue for servers like esbonio and pyright which
set the scopeUri to a directory. For `file-name-directory' to
treat the path as a directory it must include a trailing slash, a
convention which these servers do not follow.

Therefore `file-name-directory' treats the path as a file and
removes the final component, causing eglot to resolve the
configuration relative to the wrong directory.

This function fixes the issue by advising the
`eglot--workspace-configuration-plist' function, ensuring that
paths referencing directories include the trailing slash."
  (if (and path
           (file-directory-p path)
           (not (s-ends-with? "/" path)))
      (funcall orig-fun server (concat path "/"))
    (funcall orig-fun server path)))

(use-package denote
  :ensure t
  :hook ((dired-mode . denote-dired-mode))
  :config

  ;; Add reStructuredText support to denote
  (add-to-list 'denote-file-types `(rst
                                    :extension ".rst"
                                    :date-key-regexp "^:date:"
                                    :date-value-function denote-date-iso-8601
                                    :date-value-reverse-function denote-extract-date-from-front-matter
                                    :front-matter ":title: %s\n:date: %s\n:tags: %s\n:identifier: %s\n:signature: %s\n\n"
                                    :title-key-regexp "^:title:"
                                    :title-value-function identity
                                    :title-value-reverse-function denote-trim-whitespace
                                    :signature-key-regexp ":signature:"
                                    :signature-value-function identity
                                    :signature-value-reverse-function denote-trim-whitespace
                                    :keywords-key-regexp "^:tags:"
                                    :keywords-value-function ,(lambda (ks) (string-join ks ", "))
                                    :keywords-value-reverse-function denote-extract-keywords-from-front-matter
                                    :identifier-key-regexp "^:identifier:"
                                    :identifier-value-function identity
                                    :identifier-value-reverse-function denote-trim-whitespace
                                    :link ":denote:link:`%2$s <%1$s>`"
                                    :link-in-context-regexp ,(concat ":denote:link:`.*?<\\(?1:" denote-id-regexp "\\)>`"))))

(use-package denote-sequence
  :ensure t
  :after denote
  :custom
  (denote-sequence-scheme 'numeric))

(use-package eat
  :ensure t
  :hook (eshell-load . eat-eshell-mode))

(set-face-attribute 'default nil :family "UbuntuMonoNerdFont" :height 120)
(set-face-attribute 'fixed-pitch nil :family "UbuntuMonoNerdFont" :height 120)
(set-face-attribute 'variable-pitch nil :family "UbuntuSansNerdFont" :weight 'light :height 120)

(use-package nerd-icons
  :ensure t)

(use-package doric-themes :ensure t)

(use-package alc-theme
  :load-path "lisp"
  :config
  (require-theme 'doric-themes t)

  (setq alc-theme-load-light-theme-function
        (lambda () (doric-themes-select 'doric-oak))
        alc-theme-load-dark-theme-function
        (lambda () (doric-themes-select 'doric-pine)))

  (add-to-list 'after-make-frame-functions 'alc-theme-sync-to-system-theme))

(add-hook 'prog-mode-hook (lambda () (display-line-numbers-mode t)))

(setq-default display-line-numbers-widen t
              display-line-numbers-width 4)

(setq pixel-scroll-precision-use-momentum nil
      pixel-scroll-precision-interpolate-page t
      pixel-scroll-precision-momentum-seconds 0.5)
(pixel-scroll-precision-mode t)

(use-package alc-tab-bar
  :demand t
  :hook (after-init . tab-bar-mode)
  :bind (:map tab-prefix-map
         ("p" . alc-tab-bar-project-other-tab-command))
  :config
  (setq tab-bar-close-button-show nil)
  (setq tab-bar-tab-hints t)
  (setq tab-bar-auto-width nil)
  (setq tab-bar-format '(tab-bar-format-tabs-groups
                         tab-bar-separator
                         tab-bar-format-align-right
                         tab-bar-format-global
                         tab-bar-format-menu-bar))

  (setq tab-bar-tab-group-format-function 'alc-tab-bar-tab-group-format-function)
  (setq tab-bar-tab-name-format-functions '(alc-tab-bar-tab-name-format-hints
                                            tab-bar-tab-name-format-close-button
                                            tab-bar-tab-name-format-face))

  ;; Disable the menu-bar, since it's accessible via the tab bar.
  (menu-bar-mode -1))

(use-package alc-modeline
  :after alc-theme
  :load-path "lisp"
  :config
  (setq-default mode-line-format
             '("%e"
               mode-line-front-space
               alc-modeline-window-dedicated
               alc-modeline-project-identification
               "  "
               alc-modeline-remote-indication
               alc-modeline-buffer-identification
               " "
               alc-modeline-buffer-position
               "      "
               mode-line-modes
               )))

(keymap-global-set "C-x C-b" 'ibuffer)

(use-package alc-jj)
jj
config.toml

"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"

user.name = "Alex Carney"
user.email = "alcarneyme@gmail.com"

ui.editor = "emacsclient"

ui.default-command = "status"

ui.conflict-marker-style = "git"
ui.diff-formatter = ":git"

[aliases]
push-down = ["new", "-B", "@", "--no-edit"]
nvim
lua
alc
lsp
esbonio.lua

function setup(opts)
   opts = opts or {}

   if not opts.capabilities then
     opts.capabilities = vim.lsp.protocol.make_client_capabilities()
   end

   require('lspconfig').esbonio.setup {
     cmd = get_esbonio_cmd(opts),
     capabilities = capabilities,
     handlers = {
       ["sphinx/clientCreated"] = client_created,
       ["sphinx/appCreated"] = app_created,
       ["sphinx/clientErrored"] = client_errored,
       ["sphinx/clientDestroyed"] = client_destroyed,
     },
     commands = {
       EsbonioPreviewFile = {
         preview_file,
         description = 'Preview Current File',
       },
     },
     settings = {
       esbonio = {
         logging = {
           level = 'debug',
           window = opts.devtools or false,
         }
       }
     }
   }
end

function get_esbonio_cmd(opts)
  local cmd = {}

  -- TODO: Devcontainer support.
  --
  -- local workspace = require('alc.devcontainer').workspace()
  -- if workspace then
  --   table.insert(cmd, 'devcontainer')
  --   table.insert(cmd, 'exec')
  --   table.insert(cmd, '--workspace-folder')
  --   table.insert(cmd, workspace)
  -- end

  if opts.devtools then
    table.insert(cmd, 'lsp-devtools')
    table.insert(cmd, 'agent')
    table.insert(cmd, '--')
  end

  table.insert(cmd, 'esbonio')
  return cmd
end

function preview_file()
  local params = {
    command = 'esbonio.server.previewFile',
    arguments = {
      { uri = vim.uri_from_bufnr(0), show = true },
    },
  }

  local clients = require('lspconfig.util').get_lsp_clients {
    bufnr = vim.api.nvim_get_current_buf(),
    name = 'esbonio',
  }
  for _, client in ipairs(clients) do
    client.request('workspace/executeCommand', params, nil, 0)
  end

  local augroup = vim.api.nvim_create_augroup("EsbonioSyncScroll", { clear = true })
  vim.api.nvim_create_autocmd({"WinScrolled"}, {
    callback = scroll_view, group = augroup, buffer = 0
  })
end

function scroll_view(event)
  local esbonio = vim.lsp.get_active_clients({ bufnr = 0, name = 'esbonio' })[1]
  local view = vim.fn.winsaveview()

  local params = { uri = vim.uri_from_bufnr(0),  line = view.topline }
  esbonio.notify('view/scroll', params)
end

function client_created(err, result, ctx, config)
  vim.notify("Sphinx client created in " .. result.scope, vim.log.levels.INFO)
end

function app_created(err, result, ctx, config)
  vim.notify("Application created", vim.log.levels.INFO)
end

function client_errored(err, result, ctx, config)
  vim.notify("Client error: " .. result.error, vim.log.levels.ERROR)
end

function client_destroyed(err, result, ctx, config)
  vim.notify("Sphinx client destroyed", vim.log.levels.INFO)
end

return {
  setup = setup
}
init.lua

function lsp_attach(event)
  vim.keymap.set('n', 'gd', require('telescope.builtin').lsp_definitions, { buffer = event.buf })
  vim.keymap.set('n', 'gr', require('telescope.builtin').lsp_references, { buffer = event.buf })
  vim.keymap.set('n', 'gI', require('telescope.builtin').lsp_implementations, { buffer = event.buf })
  vim.keymap.set('n', '<leader>D', require('telescope.builtin').lsp_type_definitions, { buffer = event.buf })
  vim.keymap.set('n', '<leader>ds', require('telescope.builtin').lsp_document_symbols, { buffer = event.buf })
  vim.keymap.set('n', '<leader>ws', require('telescope.builtin').lsp_dynamic_workspace_symbols, { buffer = event.buf })
  vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, { buffer = event.buf })
  vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, { buffer = event.buf })
  vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, { buffer = event.buf })
end

return {
   'neovim/nvim-lspconfig',
   dependencies = {
     'hrsh7th/cmp-nvim-lsp',
     { 'j-hui/fidget.nvim', opts = {
         notification = {
           override_vim_notify = true,
         }
       }
     },
   },
   config = function()
     vim.api.nvim_create_autocmd('LspAttach', {
       group = vim.api.nvim_create_augroup('alc-lsp-attach', { clear = true }),
       callback = lsp_attach
     })

     -- Ensure the additional capabilities provided by nvim-cmp are provided to the server
     local capabilities = vim.lsp.protocol.make_client_capabilities()
     capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities())

     -- Setup each server
     local use_devtools = os.getenv('LSP_DEVTOOLS') ~= nil
     require('alc.lsp.esbonio').setup { capabilities = capabilities, devtools = use_devtools }
     require('alc.lsp.python').setup { capabilities = capabilities }
   end
}
python.lua

function setup(opts)
   opts = opts or {}

   if not opts.capabilities then
     opts.capabilities = vim.lsp.protocol.make_client_capabilities()
   end

   require('lspconfig').pyright.setup {
     capabilities = capabilities,
     settings = {
       python = {
       }
     }
   }
end

return {
  setup = setup
}
completion.lua

function setup()
  local cmp = require('cmp')

  cmp.setup {
    completion = { completeopt = 'menu,menuone,noinsert' },
    mapping = cmp.mapping.preset.insert {
       ['<C-n>'] = cmp.mapping.select_next_item(),
       ['<C-p>'] = cmp.mapping.select_prev_item(),
       ['<C-b>'] = cmp.mapping.scroll_docs(-4),
       ['<C-f>'] = cmp.mapping.scroll_docs(4),
       ['<C-y>'] = cmp.mapping.confirm { select = true },
     },
    sources = {
       { name = 'nvim_lsp' },
     },
  }
end

return {
  'hrsh7th/nvim-cmp',
  event = 'InsertEnter',
  dependencies = {
    'hrsh7th/cmp-nvim-lsp',
  },
  config = setup
}
devcontainer.lua

function workspace()
  local cwd = vim.uv.cwd()
  if not cwd then
    return nil
  end

  local repo = require('lspconfig.util').find_git_ancestor(cwd)
  if not repo then
    return nil
  end

  local devcontainer = vim.fs.joinpath(repo, '.devcontainer')
  if not vim.uv.fs_stat(devcontainer) then
    return nil
  end

  return repo
end

return {
  workspace = workspace
}
telescope.lua

return {
  'nvim-telescope/telescope.nvim',
  event = 'VimEnter',
  branch = '0.1.x',
  dependencies = {
    'nvim-lua/plenary.nvim',
    'nvim-telescope/telescope-ui-select.nvim',
  },
  config = function()
    local themes = require('telescope.themes')

    require('telescope').setup {
      defaults = themes.get_ivy(opts)
    }

    pcall(require('telescope').load_extension, 'ui-select')

    local builtin = require 'telescope.builtin'
    vim.keymap.set('n', '<leader>ff', builtin.find_files)
  end
}
init.lua

vim.opt.termguicolors = false

vim.opt.breakindent = true

vim.opt.inccommand = 'split'
vim.opt.incsearch = true
vim.opt.ignorecase = false

vim.opt.number = true

vim.opt.signcolumn = 'number'

vim.opt.list = true
vim.opt.listchars = { tab = 'ยป.', trail = 'ยท', extends = 'โ†’', precedes= 'โ†' }

vim.g.mapleader = ' '
vim.g.maplocalleader = ' '

vim.keymap.set('n', '<Esc>', '<cmd>nohlsearch<CR>')

vim.keymap.set('n', '<C-h>', '<C-w><C-h>')
vim.keymap.set('n', '<C-l>', '<C-w><C-l>')
vim.keymap.set('n', '<C-j>', '<C-w><C-j>')
vim.keymap.set('n', '<C-k>', '<C-w><C-k>')

vim.keymap.set('n', 'n', 'nzz')
vim.keymap.set('n', 'N', 'Nzz')
vim.keymap.set('n', 'G', 'Gzz')
vim.keymap.set('n', '<c-i>', '<c-i>zz')
vim.keymap.set('n', '<c-o>', '<c-o>zz')

local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
if not vim.uv.fs_stat(lazypath) then
  local lazyrepo = 'https://github.com/folke/lazy.nvim.git'
  local out = vim.fn.system({ 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath})

  if vim.v.shell_error ~= 0 then
    error('Error cloning lazy.nvim:\n' .. out)
  end
end

vim.opt.rtp:prepend(lazypath)

require('lazy').setup({
  'tpope/vim-sleuth',
  require 'alc.completion',
  require 'alc.lsp',
  require 'alc.telescope',
})
vscode
settings.json

{

"window.autoDetectColorScheme": true,
"workbench.preferredLightColorTheme": "GitHub Light Default",
"workbench.preferredDarkColorTheme": "GitHub Dark Dimmed",

"workbench.iconTheme": "material-icon-theme",

"editor.fontFamily": "'UbuntuMono NF', 'monospace', monospace",
"editor.fontSize": 12,
"editor.fontLigatures": true,
"terminal.integrated.fontFamily": "'Ubuntu Mono NF', 'monospace', monospace",
"terminal.integrated.fontSize": 12,
"terminal.integrated.lineHeight": 1,

"editor.cursorStyle": "block",
"editor.cursorBlinking": "solid",
"editor.cursorSmoothCaretAnimation": "on",
"editor.minimap.enabled": false,
"editor.smoothScrolling": true,
"debug.toolBarLocation": "commandCenter",
"window.commandCenter": true,
"window.titleBarStyle": "custom",
"workbench.activityBar.location": "top",
"workbench.editor.showTabs": "single",
"workbench.layoutControl.enabled": true,
"workbench.list.smoothScrolling": true,

"files.autoSave": "off",
"files.enableTrash": false,
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,

"files.exclude": {
   "**/.hg": true,
   "**/.git": true,
   "**/.svn": true,
   "**/*.pyc": true,
   "**/.mypy_cache": true,
   "**/__pycache__": true,
},

"[python]": {
  "editor.rulers": [ 89 ]
},

"[restructuredtext]": {
  "editor.tabSize": 3,
  "editor.wordBasedSuggestions": "off",
},

}
wezterm
commands
screenshot.lua

local wezterm = require 'wezterm'
local io = require 'io'
local os = require 'os'
local act = wezterm.action

local M = {}

local function append_tables(...)
 local result = {}
 for _, tbl in ipairs({...}) do
     for i = 1, #tbl do
         result[#result + 1] = tbl[i]
     end
 end
 return result
end

function M.setup()

  wezterm.on('augment-command-palette', function(window, pane)
    return {
      {
        brief = 'Take screenshot',
        icon = 'cod_device_camera',

        action = act.PromptInputLine {
          description = 'Screenshot name',
          -- initial_value = os.date('%Y%m%dT%H%M%S--terminal-screenshot'), -- apparently this requires a nightly build
          action = wezterm.action_callback(function(window, pane, line)
            if line then
              local dimensions = pane:get_dimensions()
              local theme = wezterm.get_builtin_color_schemes()[window:effective_config().color_scheme]
              local body = {0, "o", pane:get_lines_as_escapes()}

              local header = {
                version = 3,
                term = {
                  cols = dimensions.cols,
                  rows = dimensions.viewport_rows,
                  theme = {
                    fg = theme.foreground,
                    bg = theme.background,
                    palette = table.concat(append_tables(theme.ansi, theme.brights), ':'),
                  },
                },
              }

              local f = io.open(line .. '.cast', 'w+')
              f:write(wezterm.json_encode(header) .. '\n')
              f:write(wezterm.json_encode(body))
              f:flush()
              f:close()
            end
          end),
        },
      },
    }
  end)
end

return M
wezterm.lua

local wezterm = require 'wezterm'
local config = wezterm.config_builder()
config.keys = {}

config.font = wezterm.font('UbuntuMono Nerd Font')

if wezterm.gui.get_appearance():find("Dark") then
  config.color_scheme = 'Modus-Vivendi'
else
  config.color_scheme = 'Modus-Operandi'
end

config.use_fancy_tab_bar = true

config.default_prog = { '/usr/bin/fish', '-l' }

table.insert(config.keys, {
  key = 'Enter', mods = 'ALT', action = wezterm.action.DisableDefaultAssignment,
})

local screenshot_command = require 'commands.screenshot'
screenshot_command.setup()

return config
Makefile

.PHONY: wezterm
wezterm:
     test -L $(HOME)/.config/wezterm || ln -s $(shell pwd)/wezterm $(HOME)/.config/wezterm

.PHONY: bash
bash:
     test -L $(HOME)/.bash_profile || ln -s $(shell pwd)/bash_profile $(HOME)/.bash_profile
     test -L $(HOME)/.bashrc || ln -s $(shell pwd)/bashrc $(HOME)/.bashrc
     test -L $(HOME)/.bashrc.d || ln -s $(shell pwd)/bash $(HOME)/.bashrc.d

.PHONY: git
git:
     test -L $(HOME)/.gitconfig || ln -s $(shell pwd)/gitconfig $(HOME)/.gitconfig

.PHONY: nvim
nvim:
     test -L $(HOME)/.config/nvim || ln -s $(shell pwd)/nvim $(HOME)/.config/nvim

.PHONY: vscode
vscode:
     test -L $(HOME)/.config/Code/User || ln -s $(shell pwd)/vscode/ $(HOME)/.config/Code/User
     -code --install-extension charliermarsh.ruff
     -code --install-extension eamodio.gitlens
     -code --install-extension github.github-vscode-theme
     -code --install-extension ms-python.python
     -code --install-extension pkief.material-icon-theme
     -code --install-extension tamasfe.even-better-toml

.PHONY: emacs
emacs:
     test -L $(HOME)/.emacs.d || ln -s $(shell pwd)/emacs/ $(HOME)/.emacs.d
bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi
bashrc

if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

if [ -d ~/.bashrc.d ]; then
        for rc in ~/.bashrc.d/*; do
                if [ -f "$rc" ]; then
                        . "$rc"
                fi
        done
fi

unset rc
gitconfig

[user]
name  = Alex Carney
email = alcarneyme@gmail.com

[alias]
co = checkout
amend = commit --amend --no-edit  # Squash changes into the previous commit

hist = log --branches --remotes --tags --graph --oneline --decorate

s = !git status -sb && git --no-pager diff --shortstat

[credential "https://github.com"]
helper =
helper = !gh auth git-credential

[diff]
algorithm = histogram
colorMoved = default

[merge]
conflictstyle = diff3

[rebase]
autosquash = true
autostash = true

[commit]
verbose = true

[core]
editor = nvim

[pull]
rebase = true

[rerere]
enabled = true

[github]
user = alcarney
starship.toml

"$schema" = 'https://starship.rs/config-schema.json'

add_newline = true

[custom.jj]
ignore_timeout = true
description = "The current jj status"
when = "jj root --ignore-working-copy"
symbol = "jj "
command = '''
jj log --revisions @ --no-graph --ignore-working-copy --color always --limit 1 --template '
  separate(" ",
    change_id.shortest(8),
    bookmarks,
    "|",
    concat(
      if(conflict, "โœ˜"),
      if(divergent, "๎œฅ"),
      if(hidden, "๏Šจ"),
      if(immutable, "โ—†"),
    ),
    raw_escape_sequence("\x1b[1;32m") ++ if(empty, "(empty)") ++ raw_escape_sequence("\x1b[0m"),
    raw_escape_sequence("\x1b[1;32m") ++ coalesce(
      truncate_end(29, description.first_line(), "โ€ฆ"),
      "(no description set)",
    ) ++ raw_escape_sequence("\x1b[0m"),
  )
'
'''

[git_state]
disabled = true

[git_commit]
disabled = true

[git_metrics]
disabled = true

[git_branch]
disabled = true

[custom.git_branch]
when = true
command = "jj root --ignore-working-copy >/dev/null 2>&1 || starship module git_branch"