Skip to content

lf-araujo/workbenchless

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sane alternative to workbenches in science

Abstract | A Notebook system based on Emacs borrowing from existing config files. This system works from a single org file as an attempt to be as easy as possible for non-emacs users. It handles analyses in any language supported by org and through any server with a ssh interface. Since, its contents (an org file) are automatically rendered in Github or similar, it is trivial to share analyses results with reviewers and auditors, thus providing a way for transparent review/auditing of statistical analyses. This configuration provides a more complete set of functionality than jupyter, with less hassle.

TOC

This project is heavily inspired by existing configs:

  1. Emacs Bedrock https://codeberg.org/ashton314/emacs-bedrock and;
  2. Emacs notebook-mode https://github.com/rougier/notebook-mode.

The main motivation is the increasing pay-to-use workbenches creeping in several areas in science. The idea is that if one simple ol’ssh connection is provided, a full research environment (think RStudio server) is available from your local machine, your plots and tables are automatically pulled from the remote.

Running

  • In order to run this you need emacs -q -f org-babel-load-file --init-dir . and select this README.org, or substitute your init.el file with README.el (also provided).
  • Although most packages will be installed in the first run, pay attention to the warnings and manually install any remainder missing packages with Ctrl-Shift-P package-install. This is unlikely to happen.
  • The first run will take some time to compile packages, don’t worry.
  • Vim mode is available, but not enabled. In order to enable it, go to the vim mode section and set :eval yes.
  • All you can ever need will appear as a command after pressing the combination Ctrl-Shift-P.
  • Once done with analyses provide the link to your repo in your paper

The notebook in action

graphs/notebook.gif

The general interface of the Notebook

You can have your plots displayed

# load package and data
options(scipen=999)  # turn-off scientific notation like 1e+48
library(ggplot2)
theme_set(theme_bw())  # pre-set the bw theme.
data("midwest", package = "ggplot2")

# Scatterplot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + 
  xlim(c(0, 0.1)) + 
  ylim(c(0, 500000)) + 
  labs(subtitle="Area Vs Population", 
       y="Population", 
       x="Area", 
       title="Scatterplot", 
       caption = "Source: midwest")

plot(gg)

graphs/scatter.png

Or your tables

library(tableone)

## Load Mayo Clinic Primary Biliary Cirrhosis Data
library(survival)
data(pbc)

## Make categorical variables factors
varsToFactor <- c("status","trt","ascites","hepato","spiders","edema","stage")
pbc[varsToFactor] <- lapply(pbc[varsToFactor], factor)

## Create a variable list
dput(names(pbc))
vars <- c("time","status","age","sex","ascites","hepato",
          "spiders","edema","bili","chol","albumin",
          "copper","alk.phos","ast","trig","platelet",
          "protime","stage")

## Create Table 1 stratified by trt
tableOne <- CreateTableOne(vars = vars, strata = c("trt"), data = pbc)

## Just typing the object name will invoke the print.TableOne method
print(tableOne)
158154
2015.62 (1094.12)1996.86 (1155.93)0.883
0.894
83 (52.5)85 (55.2)
10 ( 6.3)9 ( 5.8)
65 (41.1)60 (39.0)
51.42 (11.01)48.58 (9.96)0.018
137 (86.7)139 (90.3)0.421
14 ( 8.9)10 ( 6.5)0.567
73 (46.2)87 (56.5)0.088
45 (28.5)45 (29.2)0.985
0.877
132 (83.5)131 (85.1)
16 (10.1)13 ( 8.4)
10 ( 6.3)10 ( 6.5)
2.87 (3.63)3.65 (5.28)0.131
365.01 (209.54)373.88 (252.48)0.748
3.52 (0.44)3.52 (0.40)0.874
97.64 (90.59)97.65 (80.49)0.999
2021.30 (2183.44)1943.01 (2101.69)0.747
120.21 (54.52)124.97 (58.93)0.46
124.14 (71.54)125.25 (58.52)0.886
258.75 (100.32)265.20 (90.73)0.555
10.65 (0.85)10.80 (1.14)0.197
0.201
12 ( 7.6)4 ( 2.6)
35 (22.2)32 (20.8)
56 (35.4)64 (41.6)
55 (34.8)54 (35.1)

You can leave the notebook, zoom into your code and program interactively

graphs/zoom_in.gif

You can pass org tables directly to your R/python code block

one12345
two678910
print(tab)

You can generate professional looking html reports

Call M-x notebook-export-html, this functionality was slightly modified using code from notebook mode.

FORMULAS!

$$ a=+\sqrt{2} $$

Limitations

Although knowing to work in Emacs is not required (since which-key is active, and doing anything is really a matter of hitting Ctrl-Shift-P), Emacs bindings are too complex. I am still thinking on how to make keybindings more accessible.

  • [ ] Figure out why svg-tag-mode does not load in the first try completely. Changes to the package introduced a regression whereby it again isn’t loading before notebook mode even if using demand t or ensure t
  • [ ] Magit workflow to easily share notebooks within the interface
  • [X] Figure out why eglot is not starting with ess
  • [X] Simplify keybindings. CUA mode enabled by default
  • [X] Figure out why :toc: is not autopopulating

General configuration

BEAMER export configuration example

Initial set up

Early Init

;;; early-init.el --- Minimal early init -*- lexical-binding: t; -*-

;; Speed up startup by tweaking GC and file handlers
(setq gc-cons-threshold most-positive-fixnum
      file-name-handler-alist nil)

(add-hook 'emacs-startup-hook
          (lambda ()
            (setq gc-cons-threshold (* 32 1024 1024)
                  file-name-handler-alist
                  (default-toplevel-value 'file-name-handler-alist))))

;; Disable UI elements early
(setq inhibit-startup-screen t
      tool-bar-mode nil
      menu-bar-mode nil
      scroll-bar-mode nil)
  ;; -*- lexical-binding: t -*-
  ;;; Guardrail
  (when (< emacs-major-version 29)
    (error (format "Emacs Bedrock only works with Emacs 29 and newer; you have version ~a" emacs-major-version)))


  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Basic settings
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Package initialization
  ;;
  ;; We'll stick to the built-in GNU and non-GNU ELPAs (Emacs Lisp Package
  ;; Archive) for the base install, but there are some other ELPAs you could look
  ;; at if you want more packages. MELPA in particular is very popular. See
  ;; instructions at:
  ;;
  ;;    https://melpa.org/#/getting-started
  ;;
  ;; You can simply uncomment the following if you'd like to get started with
  ;; MELPA packages quickly:
  ;;
  (with-eval-after-load 'package
    (add-to-list 'package-archives '("melpa"  . "https://melpa.org/packages/") t))

  ;; This initializes the packages for when one is reading the org file directly
  (package-initialize)

  ;; If you want to turn off the welcome screen, uncomment this
  (setopt inhibit-splash-screen t)

  ;; Native compilation settings (optional, but recommended if your Emacs was built with it)
  (setq native-comp-deferred-compilation t
        native-comp-jit-compilation t)

  (when (featurep 'native-compile)
    (setq native-comp-async-report-warnings-errors nil)
    (setq native-comp-speed 3))

  (setq native-comp-compiler-options '("-march=znver3" "-Ofast" "-g0" "-fno-finite-math-only" "-fgraphite-identity" "-floop-nest-optimize" "-fdevirtualize-at-ltrans" "-fipa-pta" "-fno-semantic-interposition" "-flto=auto" "-fuse-linker-plugin"))

  ;; Kitty image viewer
  (setq image-use-external-converter t)

  (with-eval-after-load 'org
    (setq org-file-apps
          '((auto-mode . emacs)
            ("\\.png\\'"
             . "kitty @ launch --type=window --location=vsplit --cwd=current --title=Image kitty +kitten icat --hold %s")
            ("\\.jpg\\'"
             . "kitty @ launch --type=window --location=vsplit --cwd=current --title=Image kitty +kitten icat --hold %s"))))

  ;; Recent files
  (use-package recentf
    :init (recentf-mode)
    :custom (recentf-max-menu-items 25))

  ;; Save place in files
  (save-place-mode 1)

  ;; Enable async operations in Dired
  ;(use-package async
  ;  :ensure t
  ;  :config (dired-async-mode 1))

  ;; Enable CUA mode
  (cua-mode)

  ;; Disable menu bar
  (menu-bar-mode -1)

  ;;(setopt initial-major-mode 'fundamental-mode)  ; default mode for the *scratch* buffer
  (setopt display-time-default-load-average nil) ; this information is useless for most

  ;; Automatically reread from disk if the underlying file changes
  (setopt auto-revert-avoid-polling t)
  ;; Some systems don't do file notifications well; see
  ;; https://todo.sr.ht/~ashton314/emacs-bedrock/11
  (setopt auto-revert-interval 5)
  (setopt auto-revert-check-vc-info nil)
  (global-auto-revert-mode)

  ;; Save history of minibuffer
  (setq savehist-file "~/.emacs.d/savehist"
        history-length 1000
        history-delete-duplicates t
        savehist-save-minibuffer-history t
        savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
  (savehist-mode)

  ;; Move through windows with Ctrl-<arrow keys>
  (windmove-default-keybindings 'control) ; You can use other modifiers here

  ;; Fix archaic defaults
  (setopt sentence-end-double-space nil)

  ;; Make right-click do something sensible
  (when (display-graphic-p)
    (context-menu-mode))

  ; Disable the bell
  (setq ring-bell-function 'ignore)

  (setq undo-outer-limit 72000000)

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Discovery aids
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; which-key: shows a popup of available keybindings when typing a long key
  ;; sequence (e.g. C-x ...)
  (use-package which-key
    :ensure t
    :defer t
    :config
    (which-key-mode))


  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Minibuffer/completion settings
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; For help, see: https://www.masteringemacs.org/article/understanding-minibuffer-completion

  (setopt enable-recursive-minibuffers t)                ; Use the minibuffer whilst in the minibuffer
  (setopt completion-cycle-threshold 1)                  ; TAB cycles candidates
  (setopt completions-detailed t)                        ; Show annotations
  (setopt tab-always-indent 'complete)                   ; When I hit TAB, try to complete, otherwise, indent
  (setopt completion-styles '(basic initials substring)) ; Different styles to match input to candidates

  (setopt completion-auto-help 'always)                  ; Open completion always; `lazy' another option
  (setopt completions-max-height 20)                     ; This is arbitrary
  (setopt completions-detailed t)
  (setopt completions-format 'one-column)
  ;;(setopt completions-group t)
  (setopt completion-auto-select 'second-tab)            ; Much more eager
  (setopt completion-auto-select t)                     ; See `C-h v completion-auto-select' for more possible values

  ;; (keymap-set minibuffer-mode-map "TAB" 'minibuffer-complete) ; TAB acts more like how it does in the shell

  ;; some global key bindings
  (global-set-key (kbd "C-S-p") 'execute-extended-command)
  (global-set-key (kbd "C-f") 'isearch-forward)
  (global-set-key (kbd "C-S-f") 'isearch-backward)
  (global-set-key (kbd "C-s") 'save-buffer)
  ;(define-key minibuffer-local-map (kbd "C-S-p") 'keyboard-escape-quit)

  ;; For a fancier built-in completion option, try ido-mode,
  ;; icomplete-vertical, or fido-mode. See also the file extras/base.el

  ;(icomplete-vertical-mode)
  ;(fido-vertical-mode)
  ;(setopt icomplete-delay-completions-threshold 4000)

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Interface enhancements/defaults
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Mode line information
  (setopt line-number-mode t)                        ; Show current line in modeline
  (setopt column-number-mode t)                      ; Show column as well

  (setopt x-underline-at-descent-line nil)           ; Prettier underlines
  (setopt switch-to-buffer-obey-display-actions t)   ; Make switching buffers more consistent

  (setopt show-trailing-whitespace nil)      ; By default, don't underline trailing spaces
  (setopt indicate-buffer-boundaries 'left)  ; Show buffer top and bottom in the margin

  ;; Enable horizontal scrolling
  (setopt mouse-wheel-tilt-scroll t)
  (setopt mouse-wheel-flip-direction t)

  ;; We won't set these, but they're good to know about
  ;;
  ;; (setopt indent-tabs-mode nil)
  ;; (setopt tab-width 4)

  ;; mouse in terminal
  (unless (display-graphic-p) (xterm-mouse-mode 1))

  ;; Display line numbers in programming mode
  (add-hook 'prog-mode-hook 'display-line-numbers-mode)
  (setopt display-line-numbers-width 3)           ; Set a minimum width

  ;; Nice line wrapping when working with text
  (add-hook 'text-mode-hook 'visual-line-mode)

  ;; Modes to highlight the current line with
  (let ((hl-line-hooks '(text-mode-hook prog-mode-hook)))
    (mapc (lambda (hook) (add-hook hook 'hl-line-mode)) hl-line-hooks))

  ;; remove scroll-bar
  ;;(scroll-bar-mode -1)

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Tab-bar configuration
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Show the tab-bar as soon as tab-bar functions are invoked
  ;;(setopt tab-bar-show 1)

  ;; Add the time to the tab-bar, if visible
  ;; (add-to-list 'tab-bar-format 'tab-bar-format-align-right 'append)
  ;; (add-to-list 'tab-bar-format 'tab-bar-format-global 'append)
  ;; (setopt display-time-format "%a %F %T")
  ;; (setopt display-time-interval 1)
  ;; (display-time-mode)

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Theme
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Copyright (c) 2025  Nicolas P. Rougier
  ;; Released under the GNU General Public License 3.0
  ;; Author: Nicolas P. Rougier <nicolas.rougier@inria.fr>
  ;; URL: https://github.com/rougier/nano-emacs

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;  N A N O 
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; --- Typography stack -------------------------------------------------------
  (set-face-attribute 'default nil
                      :height 140 :weight 'light :family "Roboto Mono")
  (set-face-attribute 'bold nil :weight 'regular)
  (set-face-attribute 'bold-italic nil :weight 'regular)
  (set-display-table-slot standard-display-table 'truncation (make-glyph-code ?…))
  (set-display-table-slot standard-display-table 'wrap (make-glyph-code ?–))

  ;; --- Frame / windows layout & behavior --------------------------------------
  (setq default-frame-alist
        '((height . 44) (width  . 81) (left-fringe . 0) (right-fringe . 0)
          (internal-border-width . 32) (vertical-scroll-bars . nil)
          (bottom-divider-width . 0) (right-divider-width . 0)
          (undecorated-round . t)))
  (modify-frame-parameters nil default-frame-alist)
  ;;(setq-default pop-up-windows nil)

  ;; --- Activate / Deactivate modes --------------------------------------------
  (blink-cursor-mode -1) ;;(tool-bar-mode -1) (menu-bar-mode -1)
  (global-hl-line-mode 1) (icomplete-vertical-mode 1)
  (pixel-scroll-precision-mode 1)

  ;; --- Minimal NANO (not a real) theme ----------------------------------------
  (defface nano-default '((t)) "")   (defface nano-default-i '((t)) "")
  (defface nano-highlight '((t)) "") (defface nano-highlight-i '((t)) "")
  (defface nano-subtle '((t)) "")    (defface nano-subtle-i '((t)) "")
  (defface nano-faded '((t)) "")     (defface nano-faded-i '((t)) "")
  (defface nano-salient '((t)) "")   (defface nano-salient-i '((t)) "")
  (defface nano-popout '((t)) "")    (defface nano-popout-i '((t)) "")
  (defface nano-strong '((t)) "")    (defface nano-strong-i '((t)) "")
  (defface nano-critical '((t)) "")  (defface nano-critical-i '((t)) "")

  (defun nano-set-face (name &optional foreground background weight)
    "Set NAME and NAME-i faces with given FOREGROUND, BACKGROUND and WEIGHT"

    (apply #'set-face-attribute `(,name nil
                                  ,@(when foreground `(:foreground ,foreground))
                                  ,@(when background `(:background ,background))
                                  ,@(when weight `(:weight ,weight))))
    (apply #'set-face-attribute `(,(intern (concat (symbol-name name) "-i")) nil
                                  :foreground ,(face-background 'nano-default)
                                  ,@(when foreground `(:background ,foreground))
                                  :weight regular)))

  (defun nano-link-face (sources faces &optional attributes)
    "Make FACES to inherit from SOURCES faces and unspecify ATTRIBUTES."

    (let ((attributes (or attributes
                          '( :foreground :background :family :weight
                             :height :slant :overline :underline :box))))
      (dolist (face (seq-filter #'facep faces))
        (dolist (attribute attributes)
          (set-face-attribute face nil attribute 'unspecified))
        (set-face-attribute face nil :inherit sources))))

  (defun nano-install-theme ()
    "Install THEME"

    (set-face-attribute 'default nil
                        :foreground (face-foreground 'nano-default)
                        :background (face-background 'nano-default))
    (dolist (item '((nano-default .  (variable-pitch variable-pitch-text
                                      fixed-pitch fixed-pitch-serif))
                    (nano-highlight . (hl-line highlight))
                    (nano-subtle .    (match region
                                       lazy-highlight widget-field))
                    (nano-faded .     (shadow
                                       font-lock-comment-face
                                       font-lock-doc-face
                                       icomplete-section
                                       completions-annotations))
                    (nano-popout .    (warning
                                       font-lock-string-face))
                    (nano-salient .   (success link
                                       help-argument-name
                                       custom-visibility
                                       font-lock-type-face
                                       font-lock-keyword-face
                                       font-lock-builtin-face
                                       completions-common-part))
                    (nano-strong .    (font-lock-function-name-face
                                       font-lock-variable-name-face
                                       icomplete-first-match
                                       minibuffer-prompt))
                    (nano-critical .  (error
                                       completions-first-difference))
                    (nano-faded-i .   (help-key-binding))
                    (nano-default-i . (custom-button-mouse
                                       isearch))
                    (nano-critical-i . (isearch-fail))
                    ((nano-subtle nano-strong) . (custom-button
                                                  icomplete-selected-match))
                    ((nano-faded-i nano-strong) . (show-paren-match))))
      (nano-link-face (car item) (cdr item)))

    ;; Mode & header lines 
    (set-face-attribute 'header-line nil
                        :background 'unspecified
                        :underline nil
                        :box `( :line-width 1
                                :color ,(face-background 'nano-default))
                        :inherit 'nano-subtle)
    (set-face-attribute 'mode-line nil
                        :background (face-background 'default)
                        :underline (face-foreground 'nano-faded)
                        :height 40 :overline nil :box nil)
    (set-face-attribute 'mode-line-inactive nil
                        :background (face-background 'default)
                        :underline (face-foreground 'nano-faded)
                        :height 40 :overline nil :box nil))

  (defun nano-light (&rest _args)
    "NANO light theme (based on material colors)"

    (interactive)
    (nano-set-face 'nano-default "#37474F" "#FFFFFF") ;; Blue Grey / L800
    (nano-set-face 'nano-strong "#000000" nil 'regular) ;; Black
    (nano-set-face 'nano-highlight nil "#FAFAFA") ;; Very Light Grey
    (nano-set-face 'nano-subtle nil "#ECEFF1") ;; Blue Grey / L50
    (nano-set-face 'nano-faded "#90A4AE") ;; Blue Grey / L300
    (nano-set-face 'nano-salient "#673AB7") ;; Deep Purple / L500
    (nano-set-face 'nano-popout "#FFAB91") ;; Deep Orange / L200
    (nano-set-face 'nano-critical "#FF6F00") ;; Amber / L900
    (nano-install-theme))
   ;; Sixteen (converted to hex from HSL)
  ;; blue:   #3c97dd   blue2:  #86c1b9
  ;; grey:   #b8b8b8   grey2:  #999999  grey3: #333333  grey4: #383838
  ;; grey5:  #545454   grey6:  #666666  grey7: #cccccc
  ;; orange: #f09642   orange2:#f4c725  pink:  #ba8caf
  ;; red:    #d2322d   white:  #ffffff  white2:#f7f7f7  white3:#f5f5f5
  ;; white5: #ededed   white6: #dedede  yellow:#9dbf40

  (defun nano-seventeen (&rest _args)
    "NANO light theme styled after Sublime Text 'Sixteen'."

    (interactive)
    ;; text (grey5) on white background
    (nano-set-face 'nano-default  "#545454" "#f9f9f9")  ;; fg, bg
    ;; strong accents (headings/prompts). Using Sixteen's 'pink'
    (nano-set-face 'nano-strong   "#d2322d" nil 'regular)
    ;; highlight line / selection (Sublime 'white4' approx)
    (nano-set-face 'nano-highlight nil       "#ededed")
    ;; subtle blocks/regions / selection
    (nano-set-face 'nano-subtle   nil       "#d8d8d8")  ;; light gray selection
    ;; comments/docs (Sublime 'grey' ~72%)
    (nano-set-face 'nano-faded    "#b8b8b8")
    ;; salient accents (links, types, builtins → keep Sixteen blue)
    (nano-set-face 'nano-salient  "#f09642")
    ;; pop-out items (strings in Sixteen are yellow)
    (nano-set-face 'nano-popout   "#9dbf42")
    ;; critical (errors/warnings → Sixteen red)
    (nano-set-face 'nano-critical "#d2322d")
    (nano-install-theme))

  (defun nano-dark (&rest args)
    "NANO dark theme (based on nord colors)"

    (interactive)
    (nano-set-face 'nano-default "#ECEFF4" "#2E3440") ;; Snow Storm 3
    (nano-set-face 'nano-strong "#ECEFF4" nil 'regular) ;; Polar Night 0
    (nano-set-face 'nano-highlight nil "#3B4252")  ;; Polar Night 1
    (nano-set-face 'nano-subtle nil "#434C5E") ;; Polar Night 2
    (nano-set-face 'nano-faded "#677691") ;;
    (nano-set-face 'nano-salient "#81A1C1")  ;; Frost 2
    (nano-set-face 'nano-popout "#D08770") ;; Aurora 1
    (nano-set-face 'nano-critical "#EBCB8B") ;; Aurora 2
    (nano-install-theme))

  ;; --- Command line theme chooser ---------------------------------------------
  (add-to-list 'command-switch-alist '("-dark"      . nano-dark))
  (add-to-list 'command-switch-alist '("-light"     . nano-light))
  (add-to-list 'command-switch-alist '("-seventeen" . nano-seventeen))
  (cond ((member "-dark"  command-line-args) (nano-dark))
        ((member "-light" command-line-args) (nano-light))
        (t (nano-seventeen)))

  ;;; --- Palette for terminal-like Inferior ESS (REPL) ---
  (defconst ess-term-bg        "#000000")  ;; pure black
  (defconst ess-term-fg        "#d0d0d0")  ;; soft light gray (comfortable on black)
  (defconst ess-term-border    "#303030")  ;; subtle selection/line box

  ;; Keep your Sixteen-like accents consistent
  (defconst ess16-red          "#d2322d")  ;; errors/warnings
  (defconst ess16-orange       "#f09642")  ;; numbers / salient
  (defconst ess16-yellow       "#9dbf42")  ;; strings
  (defconst ess16-blue         "#3c97dd")  ;; links/prompts
  (defconst ess16-comment      "#a8a8a8")  ;; comments (if any appear in REPL)

  (defun my/inferior-ess-terminal-dark ()
    "Render only inferior ESS buffers with terminal-like dark styling."
    (when (derived-mode-p 'inferior-ess-mode)
      ;; Preserve ANSI colors in comint output (bold, etc.)
      (ansi-color-for-comint-mode-on)
      ;; Set buffer-wide background/foreground
      (buffer-face-set `(:background ,ess-term-bg :foreground ,ess-term-fg))
      ;; Prompt and messages
      (set-face-attribute 'comint-highlight-prompt nil
                          :foreground ess16-blue :weight 'bold)
      ;; Region / selection (subtle on black)
      (face-remap-add-relative 'region `(:background ,ess-term-border))
      (face-remap-add-relative 'hl-line `(:background "#101010"))
      ;; Optional: accents for warning/error/success in REPL output
      (face-remap-add-relative 'warning `(:foreground ,ess16-orange :weight bold))
      (face-remap-add-relative 'error   `(:foreground ,ess16-red    :weight bold))
      (face-remap-add-relative 'success `(:foreground "#7bd88f"))   ;; gentle green
      ;; Optional: line spacing on pure black to reduce glare
      (setq-local line-spacing 0.15)))

  ;; Apply ONLY to the interactive ESS REPL
  (add-hook 'inferior-ess-mode-hook #'my/inferior-ess-terminal-dark)

  ;;; Important: do NOT hook ess-r-mode or ess-mode
  ;; (remove-hook 'ess-r-mode-hook #'my/ess-terminal-dark-buffer) ; if you had something before
  ;; (remove-hook 'ess-mode-hook   #'my/ess-terminal-dark-buffer)

  ;; --- Minibuffer completion --------------------------------------------------
  (setq tab-always-indent 'complete
        icomplete-delay-completions-threshold 0
        icomplete-compute-delay 0
        icomplete-show-matches-on-no-input t
        icomplete-hide-common-prefix nil
        icomplete-prospects-height 9
        icomplete-separator " . "
        icomplete-with-completion-tables t
        icomplete-in-buffer t
        icomplete-max-delay-chars 0
        icomplete-scroll t
        resize-mini-windows 'grow-only
        icomplete-matches-format nil)
  (bind-key "TAB" #'icomplete-force-complete icomplete-minibuffer-map)
  (bind-key "RET" #'icomplete-force-complete-and-exit icomplete-minibuffer-map)

  ;; --- Minimal key bindings ---------------------------------------------------
  (defun nano-quit ()
    "Quit minibuffer from anywhere (code from Protesilaos Stavrou)"

    (interactive)
    (cond ((region-active-p) (keyboard-quit))
          ((derived-mode-p 'completion-list-mode) (delete-completion-window))
          ((> (minibuffer-depth) 0) (abort-recursive-edit))
          (t (keyboard-quit))))

  (defun nano-kill ()
    "Delete frame or kill emacs if there is only one frame left"
    (interactive)
    (condition-case nil
        (delete-frame)
      (error (save-buffers-kill-terminal))))


  (bind-key "C-x k" #'kill-current-buffer)
  (bind-key "C-x C-c" #'nano-kill)
  (bind-key "C-x C-r" #'recentf-open)
  (bind-key "C-g" #'nano-quit)
  (bind-key "M-n" #'make-frame)
  (bind-key "C-z"  nil) ;; No suspend frame
  (bind-key "C-<wheel-up>" nil) ;; No text resize via mouse scroll
  (bind-key "C-<wheel-down>" nil) ;; No text resize via mouse scroll

  ;; --- Sane settings ----------------------------------------------------------
  (set-default-coding-systems 'utf-8)
  (setq-default indent-tabs-mode nil
                ring-bell-function 'ignore
                select-enable-clipboard t)

  ;; --- OSX Specific -----------------------------------------------------------
  (when (eq system-type 'darwin)
    (select-frame-set-input-focus (selected-frame))
    (setq mac-option-modifier nil
          ns-function-modifier 'super
          mac-right-command-modifier 'hyper
          mac-right-option-modifier 'alt
          mac-command-modifier 'meta))

  ;; --- Header & mode lines ----------------------------------------------------
  (setq-default mode-line-format "")
  (setq-default header-line-format
    '(:eval
      (let ((prefix (cond (buffer-read-only     '("RO" . nano-default-i))
                          ((buffer-modified-p)  '("**" . nano-critical-i))
                          (t                    '("RW" . nano-faded-i))))
            (mode (concat "(" (downcase (cond ((consp mode-name) (car mode-name))
                                              ((stringp mode-name) mode-name)
                                              (t "unknow")))
                          " mode)"))
            (coords (format-mode-line "%c:%l ")))
        (list
         (propertize " " 'face (cdr prefix)  'display '(raise -0.25))
         (propertize (car prefix) 'face (cdr prefix))
         (propertize " " 'face (cdr prefix) 'display '(raise +0.25))
         (propertize (format-mode-line " %b ") 'face 'nano-strong)
         (propertize mode 'face 'header-line)
         (propertize " " 'display `(space :align-to (- right ,(length coords))))
         (propertize coords 'face 'nano-faded)))))

  ;; --- Minibuffer setup -------------------------------------------------------
  (defun nano-minibuffer--setup ()
    (set-window-margins nil 3 0)
    (let ((inhibit-read-only t))
      (add-text-properties (point-min) (+ (point-min) 1)
        `(display ((margin left-margin)
                   ,(format "# %s" (substring (minibuffer-prompt) 0 1))))))
    (setq truncate-lines t))
  (add-hook 'minibuffer-setup-hook #'nano-minibuffer--setup)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;   Built-in customization framework
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (custom-set-variables
   ;; custom-set-variables was added by Custom.
   ;; If you edit it by hand, you could mess it up, so be careful.
   ;; Your init file should contain only one such instance.
   ;; If there is more than one, they won't work right.
   '(custom-enabled-themes '(modus-operandi))
   '(package-selected-packages
     '(meow treesit-auto htmlize ultra-scroll tree-sitter-langs tree-sitter-ess-r nim-ts-mode tree-sitter copilot editorconfig quelpa-use-package quelpa nim-mode org-roam citar ess evil which-key))
   '(package-vc-selected-packages
     '((ultra-scroll :vc-backend Git :url "https://github.com/jdtsmith/ultra-scroll"))))
  (custom-set-faces
   ;; custom-set-faces was added by Custom.
   ;; If you edit it by hand, you could mess it up, so be careful.
   ;; Your init file should contain only one such instance.
   ;; If there is more than one, they won't work right.
   )


 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Motion aids
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (use-package avy
    :ensure t
    :defer t
    :bind (("C-c j" . avy-goto-line)
           ("S-j"   . avy-goto-char-timer)))

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;
  ;;;   Power-ups: Embark and Consult
  ;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Consult: Misc. enhanced commands
  (use-package consult
    :ensure t
    :defer t
    :bind (
           ;; Drop-in replacements
           ("C-x b" . consult-buffer)     ; orig. switch-to-buffer
           ("M-y"   . consult-yank-pop)   ; orig. yank-pop
           ;; Searching
           ("M-s r" . consult-ripgrep)
           ("M-s l" . consult-line)       ; Alternative: rebind C-s to use
           ("M-s s" . consult-line)       ; consult-line instead of isearch, bind
           ("M-s L" . consult-line-multi) ; isearch to M-s s
           ("M-s o" . consult-outline)
           ;; Isearch integration
           :map isearch-mode-map
           ("M-e" . consult-isearch-history)   ; orig. isearch-edit-string
           ("M-s e" . consult-isearch-history) ; orig. isearch-edit-string
           ("M-s l" . consult-line)            ; needed by consult-line to detect isearch
           ("M-s L" . consult-line-multi)      ; needed by consult-line to detect isearch
           )
    :config
    ;; Narrowing lets you restrict results to certain groups of candidates
    (setq consult-narrow-key "<"))

  (use-package embark
    :ensure t
    :demand t
    :after avy
    :defer t
    :bind (("C-c a" . embark-act))        ; bind this to an easy key to hit
    :init
    ;; Add the option to run embark when using avy
    (defun bedrock/avy-action-embark (pt)
      (unwind-protect
          (save-excursion
            (goto-char pt)
            (embark-act))
        (select-window
         (cdr (ring-ref avy-ring 0))))
      t)

    ;; After invoking avy-goto-char-timer, hit "." to run embark at the next
    ;; candidate you select
    (setf (alist-get ?. avy-dispatch-alist) 'bedrock/avy-action-embark))

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

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ;;;
        ;;;   Minibuffer and completion
        ;;;
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Marginalia: annotations for minibuffer
  (use-package marginalia
    :ensure t
    :defer t
    :config
    (marginalia-mode))

  ;; Popup completion-at-point
  (use-package corfu
    :ensure t
    :defer t
    :init
    (global-corfu-mode t)
    :custom
    (corfu-auto t
        corfu-auto-delay 0.05                   ;; low delay; tweak to taste
        corfu-auto-prefix 1                     ;; trigger after 1 char
        corfu-quit-no-match t                   ;; close if nothing to show
        corfu-preselect 'prompt)
    :bind
    (:map corfu-map
          ("SPC" . corfu-insert-separator)
          ("C-n" . corfu-next)
          ("C-p" . corfu-previous)))

  ;; Part of corfu
  (use-package corfu-popupinfo
    :after corfu
    :defer t
    :hook (corfu-mode . corfu-popupinfo-mode)
    :custom
    ;(corfu-popupinfo-delay '(0.25 . 0.1))
    (corfu-popupinfo-hide nil)
    :config
    (corfu-popupinfo-mode))

  ;; Make corfu popup come up in terminal overlay
  (use-package corfu-terminal
    :if (not (display-graphic-p))
    :ensure t
    :defer t
    :config
    (corfu-terminal-mode))

  ;; Fancy completion-at-point functions; there's too much in the cape package to
  ;; configure here; dive in when you're comfortable!
  (use-package cape
    :ensure t
    ;;:defer t
    :init
    (add-to-list 'completion-at-point-functions #'cape-dabbrev)
    (add-to-list 'completion-at-point-functions #'cape-file)
  :hook
  ;;(ess-mode . mpger/cape-capf-ess)
  (ess-r-mode . mpger/cape-capf-ess)
  (ess-r-inferior-mode . mpger/cape-capf-ess)
    )

  ;; Pretty icons for corfu
  (use-package kind-icon
    :if (display-graphic-p)
    :ensure t
    :after corfu
    :config
    (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

  (use-package eshell
    :defer t
    :init
    (defun bedrock/setup-eshell ()
      ;; Something funny is going on with how Eshell sets up its keymaps; this is
      ;; a work-around to make C-r bound in the keymap
      (keymap-set eshell-mode-map "C-r" 'consult-history))
    :hook ((eshell-mode . bedrock/setup-eshell)))

  ;; Orderless: powerful completion style
  (use-package orderless
    :ensure t
    :defer t
    :config
    (setq completion-styles '(orderless)))

        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ;;;
        ;;;   Misc. editing enhancements
        ;;;
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;; Modify search results en masse
  (use-package wgrep
    :ensure t
    :defer t
    :config
    (setq wgrep-auto-save-buffer t))

  (which-key-show-top-level)

Configure TRAMP to your server

;; Fast VC on remote files
(setq vc-ignore-dir-regexp
      (format "\\(%s\\)\\|\\(%s\\)"
              vc-ignore-dir-regexp
              tramp-file-name-regexp))
(setq vc-handled-backends '(Git))       ;; Avoid scanning many VC backends
(setq remote-file-name-inhibit-cache 60)
(setq tramp-verbose 0)
(setq tramp-ssh-controlmaster-options
      "-o ControlPath=/tmp/ssh-ControlPath-%r@%h:%p -o ControlMaster=auto -o ControlPersist=yes")

(with-eval-after-load 'tramp
  ;; Your custom tramp methods:
  (add-to-list 'tramp-methods
               `("magma"
                 (tramp-login-program "qsub")
                 (tramp-login-args (("-I" "-q" "magma" "-lnodes=giant")))
                 (tramp-login-env (("SHELL") ("/bin/sh")))
                 (tramp-remote-shell "/bin/sh")
                 (tramp-remote-shell-args ("-c"))
                 (tramp-connection-timeout 20)))
  (add-to-list 'tramp-methods
               `("workq"
                 (tramp-login-program "qsub")
                 (tramp-login-args (("-I" "-q" "workq" "-l" "ncpus=23")))
                 (tramp-login-env (("SHELL") ("/bin/sh")))
                 (tramp-remote-shell "/bin/sh")
                 (tramp-remote-shell-args ("-c"))
                 (tramp-connection-timeout 20))))

Developer amenities

;;; This will try to use tree-sitter modes for many languages. Please run
;;;
;;;   M-x treesit-install-language-grammar
;;;
;;; Before trying to use a treesit mode.

;;; Contents:
;;;
;;;  - Built-in config for developers
;;;  - Version Control
;;;  - Common file types
;;;  - Eglot, the built-in LSP client for Emacs

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;   Built-in config for developers
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(setq treesit-language-source-alist
      '((r . ("https://github.com/r-lib/tree-sitter-r" "main" "src"))
        (nim . ("https://github.com/alaviss/tree-sitter-nim" "main" "src"))))

(use-package emacs
  :config
  ;; Treesitter config
  ;; Tell Emacs to prefer the treesitter mode
  ;; You'll want to run the command `M-x treesit-install-language-grammar' before editing.
  (setq major-mode-remap-alist
	'((yaml-mode . yaml-ts-mode)
	  (bash-mode . bash-ts-mode)
	  (js2-mode . js-ts-mode)
	  (typescript-mode . typescript-ts-mode)
	  (json-mode . json-ts-mode)
	  (css-mode . css-ts-mode)
	  (ess-r-mode . r-mode)
	  (inferior-ess-r-mode . r-mode)
	  (nim-mode . nim-ts-mode)
	  (python-mode . python-ts-mode)))
  :hook
  ;; Auto parenthesis matching
  (prog-mode . electric-pair-mode))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;   Version Control
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Magit: best Git client to ever exist
(use-package magit
  :defer t
  :ensure t
  :bind (("C-x g" . magit-status))
  :commands (magit-status magit-blame))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;   Common file types
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(use-package markdown-mode
  :defer t
  :hook ((markdown-mode . visual-line-mode)))

(use-package yaml-mode
  :defer t
  :ensure t)

(use-package json-mode
  :defer t
  :ensure t)

;; Emacs ships with a lot of popular programming language modes. If it's not
;; built in, you're almost certain to find a mode for the language you're
;; looking for with a quick Internet search.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;   Eglot, the built-in LSP client for Emacs
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Helpful resources:
;;
;;  - https://www.masteringemacs.org/article/seamlessly-merge-multiple-documentation-sources-eldoc

(use-package eglot
  ;; no :ensure t here because it's built-in
  :defer t
  ;; Configure hooks to automatically turn-on eglot for selected modes
  :custom
  (eglot-send-changes-idle-time 0.4)
  (eglot-report-progress nil)          ; suppress nimlangserver progress spam
  (jsonrpc-inhibit-debug-logs t)
  (eglot-extend-to-xref t)             ; activate Eglot in referenced non-project files
  :hook
  ((ess-r-mode-inferior . eglot-ensure)
   (nim-ts-mode         . eglot-ensure))
  :config
  (fset #'jsonrpc--log-event #'ignore)  ; massive perf boost---don't log every event
  (setq eldoc-echo-area-use-multiline-p nil)  ; Disable multiline echo area messages
  ;; Sometimes you need to tell Eglot where to find the language server
  (add-to-list 'eglot-server-programs
               '(nim-ts-mode . ("~/.nimble/bin/nimlangserver" "--autobind")))
  ;; Point nimlangserver at the correct nimsuggest (matches active nim 2.3.1 compiler)
  ;; Without this, it falls back to /usr/bin/nimsuggest (2.2.0) → 0 completions
  (setq-default eglot-workspace-configuration
                '((:nim . (:nimsuggestPath "~/.nimble/bin/nimsuggest"))))

  )

(defun mpger/cape-capf-ess ()
  (dolist (elmnt
	   (mapcar #'cape-company-to-capf
		   (list #'company-R-library #'company-R-args #'company-R-objects)))
    (add-to-list 'completion-at-point-functions elmnt))
  (setq-local orderless-matching-styles '(orderless-literal))) ;; default is '(orderless-literal orderless-regexp)

Citation management

(use-package citar
  :ensure t
  :bind (("C-c b" . citar-insert-citation)
	   :map minibuffer-local-map
	   ("M-b" . citar-insert-preset))
  :custom
  ;; Allows you to customize what citar-open does
  (citar-file-open-functions '(("html" . citar-file-open-external)
				 ;; ("pdf" . citar-file-open-external)
				 (t . find-file))))

;; Optional: if you have the embark package installed, enable the ability to act
;; on citations with Citar by invoking `embark-act'.
(use-package citar-embark
  :ensure t
  :after citar embark
  :diminish ""
  :no-require
  :config (citar-embark-mode))
(setq org-latex-pdf-process
      '("lualatex -shell-escape -interaction=nonstopmode -output-directory=%o %f"
        "lualatex -shell-escape -interaction=nonstopmode -output-directory=%o %f"))

(use-package citar-org-roam
  :defer t
  :diminish ""
  ;; To get this to work both Citar *and* Org-roam have to have been used
  :after citar org-roam
  :no-require
  :config
  (citar-org-roam-mode)
  (setq citar-org-roam-note-title-template "${author} - ${title}\n#+filetags: ${tags}"))

Vim mode

(use-package evil
  :ensure t

  :init
  (setq evil-respect-visual-line-mode t)
  (setq evil-undo-system 'undo-redo)

  ;; Enable this if you want C-u to scroll up, more like pure Vim
  ;(setq evil-want-C-u-scroll t)

  :config
  (evil-mode)
; replace only in visual selection
(setq evil-ex-visual-char-range t)  ; built in solution for single line
; leader key (largely used to replace C-x <stuff> commands)
(evil-set-leader nil (kbd "SPC"))
;(evil-define-key 'normal 'global (kbd "<leader>c")  'execute-extended-command)
;;(global-set-key (kbd "C-S-p") 'execute-extended-command)
;   window movement
(evil-define-key 'normal 'global (kbd "<leader>wo")  'other-window)
(evil-define-key 'normal 'global (kbd "<leader>wh") 'windmove-left)
(evil-define-key 'normal 'global (kbd "<leader>wj") 'windmove-down)
(evil-define-key 'normal 'global (kbd "<leader>wk") 'windmove-up)
(evil-define-key 'normal 'global (kbd "<leader>wl") 'windmove-right)
;   buffer select, buffer list, find file, delete window
(evil-define-key 'normal 'global (kbd "<leader>bb") 'switch-to-buffer)
(evil-define-key 'normal 'global (kbd "<leader>bB") 'list-buffers)
(evil-define-key 'normal 'global (kbd "<leader>ff") 'find-file)
(evil-define-key 'normal 'global (kbd "<leader>fr")  'recentf-open)
(evil-define-key 'normal 'global (kbd "<leader>fe")
  (lambda () (interactive) (find-file user-init-file)))
(evil-define-key 'normal 'global (kbd "<leader>0") 'delete-window)
(evil-define-key 'normal 'global (kbd "<leader>bk") 'kill-current-buffer)
(evil-define-key 'normal 'global (kbd "<leader>qq") 'nano-quit)
(evil-define-key 'normal 'global (kbd "<leader>qk") 'nano-kill)
(evil-define-key 'normal 'global (kbd "<leader>os") 'org-edit-src-exit)
(evil-define-key 'normal 'global (kbd "<leader>oq") 'org-edit-src-abort)
(evil-define-key 'normal 'global (kbd "<leader><SPC>") 'ess-eval-region-or-function-or-paragraph-and-step)
;   definition jumping (gd already goes to definition)
(evil-define-key 'normal 'global (kbd "gD") 'xref-pop-marker-stack)
;   allow replacement only in selection for visual block mode
(evil-define-key 'visual 'global (kbd "<leader>vbr")
                               'evil-visual-replace-replace-regexp)
;   commenting
(evil-define-key '(normal visual) 'global (kbd "gc") 'comment-region)
;   indentation
(evil-define-key '(normal visual) 'global (kbd "gi") 'indent-region)
;  org
(evil-define-key 'normal 'global (kbd "<leader>obt") 'org-toggle-blocks)
;   statusline commands (available as ":<command>")
(evil-ex-define-cmd "done" 'save-buffers-kill-emacs)
(evil-ex-define-cmd "at"   'open-ansi-term)
(evil-ex-define-cmd "rb"   'rename-buffer)  
(evil-ex-define-cmd "hsp"  'split-window-below)
(evil-ex-define-cmd "sw"   'rotate-frame)
(evil-ex-define-cmd "tp"   'transpose-frame)
(evil-ex-define-cmd "rshp" 'reshape-window)
  ;; Configuring initial major mode for some modes
  (evil-set-initial-state 'vterm-mode 'emacs))

Org mode

(use-package org
  :ensure t
  :defer t
  :hook ((org-mode . visual-line-mode)  ; wrap lines at word breaks
	 (org-mode . flyspell-mode))    ; spell checking!
	;; (org-mode . toc-org-mode)
        ;; (org-mode . svg-tag-mode))    ; notebook mode

  :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
  :init
  (setq  org-startup-with-inline-images 'inlineimages)
  (setq org-image-actual-width `( ,(truncate (* (frame-pixel-width) 0.85))))
  (setq org-confirm-babel-evaluate nil)
  (setq org-format-latex-options (plist-put nil :scale 2.0))

  :custom
  (org-display-remote-inline-images 'download)

  :config
  (require 'oc-csl)                     ; citation support
  (add-to-list 'org-export-backends 'md)

  ;; Make org-open-at-point follow file links in the same window
  (setf (cdr (assoc 'file org-link-frame-setup)) 'find-file)

  ;; Make exporting quotes better
  (setq org-export-with-smart-quotes t)

  ;; toggle blocks
  (defvar org-blocks-hidden nil)

  (defun org-toggle-blocks ()
    (interactive)
    (if org-blocks-hidden
	(org-show-block-all)
      (org-hide-block-all))
    (setq-local org-blocks-hidden (not org-blocks-hidden)))

  (add-hook 'org-mode-hook 'org-toggle-blocks)

 (defvar my/org-babel-lazy-languages
   '((R . ob-R)
     (julia . ob-julia)
     (python . ob-python)
     (shell . ob-shell)
     (emacs-lisp . ob-emacs-lisp)
     (lisp . ob-lisp)
     (scheme . ob-scheme)
     (sql . ob-sql)
     (awk . ob-awk)
     (C . ob-C)
     (cpp . ob-C)
     (js . ob-js)
     (java . ob-java)
     (jupyter . ob-jupyter)
     (latex . ob-latex)
     (matlab . ob-matlab)
     (octave . ob-octave)
     (perl . ob-perl)
     (ruby . ob-ruby)
     (scala . ob-scala)
     (sed . ob-sed)
     (haskell . ob-haskell)
     (fortran . ob-fortran)
     (go . ob-go)
     (groovy . ob-groovy)
     (maxima . ob-maxima)
     (makefile . ob-makefile)
     (dot . ob-dot)
     (plantuml . ob-plantuml)
     (gnuplot . ob-gnuplot)
     (ledger . ob-ledger)
     (screen . ob-screen)
     (sql . ob-sql)
     (restclient . ob-restclient))
   "Mapping of Org Babel languages to their corresponding features.")

 (defun my/org-babel-load-language-if-needed (lang)
   "Load Org Babel language LANG if not already loaded."
   (unless (assoc lang org-babel-load-languages)
     (let ((feature (cdr (assoc lang my/org-babel-lazy-languages))))
       (when feature
         (require feature)
         (setq org-babel-load-languages
               (cons (cons lang t) org-babel-load-languages))
         (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)))))

 (defun my/org-babel-get-src-lang ()
   "Get the language of the current Org Babel block."
   (let ((info (org-babel-get-src-block-info 'light)))
     (when info
       (intern (car info)))))

 (defun my/org-babel--maybe-load-lang (info)
  "Ensure the language in INFO is loaded before execution."
  (let ((lang (intern (car info))))
    (my/org-babel-load-language-if-needed lang))
  info)

(advice-add 'org-babel-get-src-block-info :filter-return #'my/org-babel--maybe-load-lang)

  (defun ek/babel-ansi ()
    (when-let ((beg (org-babel-where-is-src-block-result nil nil)))
      (save-excursion
	(goto-char beg)
	(when (looking-at org-babel-result-regexp)
	  (let ((end (org-babel-result-end))
		(ansi-color-context-region nil))
	    (ansi-color-apply-on-region beg end))))))
  (add-hook 'org-babel-after-execute-hook 'ek/babel-ansi)


  (define-key org-mode-map (kbd "$")
	      (lambda ()
		(interactive)
		(insert "$")
		(save-excursion
		  (backward-char 1)
		  (if (org-inside-LaTeX-fragment-p)
		      (progn
			(forward-char 2)
			(org-preview-latex-fragment))))))

  )

;; THIS SECTION IS FOR THE HTML EMBEDDED EXPORT
(require 'base64)
(with-eval-after-load 'ox-html
  (advice-add 'org-html--format-image :override #'orgTZA-html--format-image))

(defcustom org-html-image-base64-max-size #x40000
  "Export embedded base64 encoded images up to this size."
  :type 'number
  :group 'org-export-html)

(defun file-to-base64-string (file &optional image prefix postfix)
  "Transform binary file FILE into a base64-string prepending PREFIX and appending POSTFIX.
		  Puts \"data:image/%s;base64,\" with %s replaced by the image type before the actual image data if IMAGE is non-nil."
  (concat prefix
	  (with-temp-buffer
	    (set-buffer-multibyte nil)
	    (insert-file-contents file nil nil nil t)
	    (base64-encode-region (point-min) (point-max) 'no-line-break)
	    (when image
	      (goto-char (point-min))
	      (insert (format "data:image/%s;base64," (image-type-from-file-name file))))
	    (buffer-string))
	  postfix))


(defun orgTZA-html-base64-encode-p (file)
  "Check whether FILE should be exported base64-encoded.
    The return value is actually FILE with \"file://\" removed if it is a prefix of FILE."
  (when (and (stringp file)
	     (string-match "\\`file:" file))
    (if (string-match "\\`file://ssh" file)
	(setq file (replace-regexp-in-string "\\`file://ssh" "/ssh" file))
      (setq file (substring file (match-end 0)))))
  (and
   (file-readable-p file)
   (let ((size (nth 7 (file-attributes file))))
     (<= size org-html-image-base64-max-size))
   file))


(defun orgTZA-html--format-image (source attributes info)
  "Return \"img\" tag with given SOURCE and ATTRIBUTES.
		  SOURCE is a string specifying the location of the image.
		  ATTRIBUTES is a plist, as returned by
		  `org-export-read-attribute'.  INFO is a plist used as
		  a communication channel."
  (if (string= "svg" (file-name-extension source))
      (org-html--svg-image source attributes info)
    (let* ((file (orgTZA-html-base64-encode-p source))
	   (data (if file (file-to-base64-string file t)
		   source)))
      (org-html-close-tag
       "img"
       (org-html--make-attribute-string
	(org-combine-plists
	 (list :src data
	       :alt (if (string-match-p "^ltxpng/" source)
			(org-html-encode-plain-text
			 (org-find-text-property-in-string 'org-latex-src source))
		      (file-name-nondirectory source)))
	 attributes))
       info))))

;; END THIS SECTION IS FOR THE HTML EMBEDDED EXPORT

;(use-package toc-org
;  :ensure t
;  :after org)

(setq org-latex-listings 'listings)           ;; older setting

Notebook mode

Unfortunately svg-tag-mode is not automatically installed at the moment, open this org file and C-c C-c in this section and it will start working. The isssue now seems to be related to the async package that improves loading speed but causes this issue.

(use-package svg-tag-mode
  :ensure t
  :demand t)

(require 'svg-tag-mode)

;; Prevent errors if something tries to set svg-tag-tags early
;;(unless (boundp 'svg-tag-tags)
 ;; (setq svg-tag-tags nil))

        (defgroup notebook nil
            "Customization options for `notebook-mode'."
            :group 'org)

          (defcustom notebook-babel-python-command
            "/opt/anaconda3/bin/python"
            "Python interpreter's path."
            :group 'notebook)

          (defcustom notebook-cite-csl-styles-dir
            "."
            "CSL styles citations' directory."
            :group 'notebook)

        (defcustom notebook-tags
          '(
            ;; Inline code
            ;; --------------------------------------------------------------------
            ("^#\\+call:" .     ((lambda (tag) (svg-tag-make "CALL"
                                                             :face 'org-meta-line))
                                 (lambda () (interactive) (notebook-call-at-point)) "Call function"))
            ("call_" .         ((lambda (tag) (svg-tag-make "CALL"
                                                            :face 'default
                                                            :margin 1
                                                            :alignment 0))
                                (lambda () (interactive) (notebook-call-at-point)) "Call function"))
            ("src_" .          ((lambda (tag) (svg-tag-make "CALL"
                                                            :face 'default
                                                            :margin 1
                                                            :alignment 0))
                                (lambda () (interactive) (notebook-call-at-point)) "Execute code"))

            ;; Code blocks
            ;; --------------------------------------------------------------------
            ("^#\\+begin_src\\( [a-zA-Z\-]+\\)" .  ((lambda (tag)
                                                      (svg-tag-make (upcase tag)
                                                                    :face 'org-meta-line
                                                                    :crop-left t))))
            ("^#\\+begin_src" . ((lambda (tag) (svg-tag-make "RUN"
                                                             :face 'org-meta-line
                                                             :inverse t
                                                             :crop-right t))
                                 (lambda () (interactive) (notebook-run-at-point)) "Run code block"))
            ("^#\\+end_src" .    ((lambda (tag) (svg-tag-make "END"
                                                              :face 'org-meta-line))))
            (":session" . ((lambda (tag) (svg-tag-make "ZOOM-IN"
                                                       :face 'org-meta-line
                                                       :inverse t
                                                       :crop-right t))
                           (lambda () (interactive) (org-edit-src-code)) "Zoom-in"))



            ;; Export blocks
            ;; --------------------------------------------------------------------
            ("^#\\+begin_export" . ((lambda (tag) (svg-tag-make "EXPORT"
                                                                :face 'org-meta-line
                                                                :inverse t
                                                                :alignment 0
                                                                :crop-right t))))
            ("^#\\+begin_export\\( [a-zA-Z\-]+\\)" .  ((lambda (tag)
                                                         (svg-tag-make (upcase tag)
                                                                       :face 'org-meta-line
                                                                       :crop-left t))))
            ("^#\\+end_export" . ((lambda (tag) (svg-tag-make "END"
                                                              :face 'org-meta-line))))

            ;; :noexport: tag
            ;; --------------------------------------------------------------------
            ("\\(:no\\)export:" .    ((lambda (tag) (svg-tag-make "NO"
                                                                  :face 'org-meta-line
                                                                  :inverse t
                                                                  :crop-right t))))
            (":no\\(export:\\)" .    ((lambda (tag) (svg-tag-make "EXPORT"
                                                                  :face 'org-meta-line
                                                                  :crop-left t))))

            ;; Miscellaneous keywords
            ;; --------------------------------------------------------------------
            ("|RUN|" .          ((lambda (tag) (svg-tag-make "RUN"
                                                             :face 'org-meta-line
                                                             :inverse t))))
            ("|RUN ALL|" .       ((lambda (tag) (svg-tag-make "RUN ALL"
                                                              :face 'org-meta-line))
                                  (lambda () (interactive) (notebook-run)) "Run all notebook code blocks"))
            ("|SETUP|" .         ((lambda (tag) (svg-tag-make "SETUP"
                                                              :face 'org-meta-line))
                                  (lambda () (interactive) (org-sbe "setup")) "Setup notebook environment"))
            ("|ZOOM-IN-CODE|" .       ((lambda (tag) (svg-tag-make "ZOOM-IN"
                                                                   :face 'org-meta-line))
                                       (lambda () (interactive) (org-edit-src-code)) "Zoom-in"))

            ("|EXPORT|" .        ((lambda (tag) (svg-tag-make "EXPORT"
                                                              :face 'org-meta-line))
                                  (lambda () (interactive) (notebook-export-html)) "Export the notebook to HTML"))
            ("|CALL|" .          ((lambda (tag) (svg-tag-make "CALL"
                                                              :face 'org-meta-line))))


            ;; References
            ;; --------------------------------------------------------------------
            ("\\(\\[cite:@[A-Za-z]+:\\)" .
             ((lambda (tag) (svg-tag-make (upcase tag)
                                                ;            :face 'nano-default
                                          :inverse t
                                          :beg 7 :end -1
                                          :crop-right t))))
            ("\\[cite:@[A-Za-z]+:\\([0-9a-z]+\\]\\)" .
             ((lambda (tag) (svg-tag-make (upcase tag)
                                                ;            :face 'nano-default
                                          :end -1
                                          :crop-left t))))

            ;; Miscellaneous properties
            ;; --------------------------------------------------------------------
            ("^#\\+caption:" .   ((lambda (tag) (svg-tag-make "CAPTION"
                                                              :face 'org-meta-line))))
            ("^#\\+latex:" .     ((lambda (tag) (svg-tag-make "LATEX"
                                                              :face 'org-meta-line))))
            ("^#\\+html:" .      ((lambda (tag) (svg-tag-make "HTML"
                                                              :face 'org-meta-line))))
            ("^#\\+name:" .      ((lambda (tag) (svg-tag-make "NAME"
                                                              :face 'org-meta-line))))
            ("^#\\+header:" .    ((lambda (tag) (svg-tag-make "HEADER"
                                                              :face 'org-meta-line))))
            ("^#\\+label:" .     ((lambda (tag) (svg-tag-make "LABEL"
                                                              :face 'org-meta-line))))
            ("^#\\+results:"  .  ((lambda (tag) (svg-tag-make "RESULTS"
                                                              :face 'org-meta-line)))))
          "The `notebook-mode' tags alist.
               This alist is the `notebook-mode' specific tags list.  It follows the
               same definition pattern as the `svg-tag-tags' alist (to which
               `notebook-tags' is added)."
          :group 'notebook)


          (defcustom notebook-font-lock-case-insensitive t
            "Make the keywords fontification case insensitive if non-nil."
            :group 'notebook)

          (defcustom notebook-indent t
            "Default document indentation.
                 If non-nil, `org-indent' is called when the mode is turned on."
            :group 'notebook)

          (defcustom notebook-hide-blocks t
            "Default visibility of org blocks in `notebook-mode'.
                 If non-nil, the org blocks are hidden when the mode is turned on."
            :group 'notebook)

          (defun notebook-run-at-point ()
            "Update notebook rendering at point."
            (interactive)
            (org-ctrl-c-ctrl-c)
            (org-redisplay-inline-images))

          (defalias 'notebook-call-at-point 'org-ctrl-c-ctrl-c)

          (defun notebook-setup ()
            "Notebook mode setup function."
            (interactive)
            (setq org-cite-csl-styles-dir notebook-cite-csl-styles-dir)
            (setq org-babel-python-command notebook-babel-python-command)
            (require 'ob-python)
            (require 'oc-csl))

          (defalias 'notebook-run 'org-babel-execute-buffer)

          (defalias 'notebook-export-html 'org-html-export-to-html)

          (defun notebook-mode-on ()
            "Activate notebook mode."

            (add-to-list 'font-lock-extra-managed-props 'display)
            (setq font-lock-keywords-case-fold-search notebook-font-lock-case-insensitive)
            (setq org-image-actual-width `( ,(truncate (* (frame-pixel-width) 0.85))))
            (setq org-startup-with-inline-images t)
            ;(require 'svg-tag-mode)
            (mapc #'(lambda (tag) (add-to-list 'svg-tag-tags tag)) notebook-tags)
            (org-redisplay-inline-images)
            (if notebook-indent (org-indent-mode))
            (if notebook-hide-blocks (org-hide-block-all))
            (add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images)
            (svg-tag-mode 1)
            (message "notebook mode on"))

          (defun notebook-mode-off ()
            "Deactivate notebook mode."

            (svg-tag-mode -1)
            (if notebook-indent (org-indent-mode -1))
            (if notebook-hide-blocks (org-hide-block-all))
            (remove-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images))

             ;;; autoload
          (define-minor-mode notebook-mode
            "Minor mode for graphical tag as rounded box."
            :group 'notebook
            (if notebook-mode
                (notebook-mode-on)
              (notebook-mode-off)))

          (define-globalized-minor-mode
            global-notebook-mode notebook-mode notebook-mode-on)

        (add-hook 'org-mode-hook #'notebook-mode)

Copilot and AI

(use-package org-ai
  :defer t
  :ensure t)

(setq org-ai-openai-api-token "your token")


(use-package copilot
  :defer t
  :commands (copilot-mode copilot-accept-completion copilot-accept-completion-by-word)
  :bind (:map copilot-completion-map
              ("C-<right>" . 'copilot-accept-completion))
  :config
  (defun my/conditionally-enable-copilot ()
  "Enable copilot-mode only in appropriate buffers."
  (when (and (derived-mode-p 'prog-mode)
             (not (string= (buffer-name) "*scratch*"))
             (buffer-file-name)) ;; only in file-backed buffers
    (copilot-mode)))

(add-hook 'after-change-major-mode-hook #'my/conditionally-enable-copilot)
  )

ESS

(use-package ess
  :ensure t
  :defer t
  :commands (ess-r-mode ess-switch-to-inferior-or-script-buffer ess-rdired ess-get-process)
  :hook
  (ess-mode . (lambda ()
                (eglot-ensure)
                (make-local-variable 'company-backends)))
  :config
  (require 'ess-site)
  (setq ess-use-flymake nil)
  (setq ess-use-company 'scriptonly)
  (setq ess-indent-with-fancy-comments nil)
  (setq ess-history-directory "~/.cache")
  (setq ess-R-font-lock-keywords
	'((ess-R-fl-keyword:keywords . t)
	  (ess-R-fl-keyword:constants . t)
	  (ess-R-fl-keyword:modifiers . t)
	  (ess-R-fl-keyword:fun-defs . t)
	  (ess-R-fl-keyword:assign-ops . t)
	  (ess-R-fl-keyword:%op% . t)
	  (ess-fl-keyword:fun-calls . t)
	  (ess-fl-keyword:numbers . t)
	  (ess-fl-keyword:operators)
	  (ess-fl-keyword:delimiters)
	  (ess-fl-keyword:=)
	  (ess-R-fl-keyword:F&T . t)))
  (setq ess-help-own-frame 'one)  ; avoid destroying existing frame
  (setq ess-help-reuse-window t)  ; same above
  (setq comint-scroll-to-bottom-on-input t)
  (setq comint-scroll-to-bottom-on-output t)
  (setq comint-move-point-for-output t)
  (setq comint-scroll-show-maximum-output t)

  (setq ess-ask-for-ess-directory nil)
  (setq ess-startup-directory 'default-directory)

  ;; Trying to speed up ess on orgmode
  (setq ess-eval-visibly-p 'nowait)

  (setq display-buffer-alist
	'(("^\\*R[:\\*]" . (display-buffer-in-side-window
			    (side . bottom)
			    (slot . -1)
			    ))
	  ("^\\*R dired\\*" . (display-buffer-in-side-window
			       (side . right)
			       (slot . -1)
			       (window-width . 0.25)))
	  ("^\\*help\\[R\\]" . (display-buffer-in-side-window
				(side . right)
				(slot . 1)
				(window-width . 0.33)))))

  (define-key comint-mode-map (kbd "<up>") 'comint-previous-matching-input-from-input)
  (define-key comint-mode-map (kbd "<down>") 'comint-next-matching-input-from-input)
    )
(defun sloth/org-babel-edit-prep (info)
  (let* ((proc (ess-get-process))
         (proc-name (if proc
                        (process-name proc)
                      "R-default")))
    (setq buffer-file-name
          (format "org-src-%s.org" proc-name)))
  (eglot-ensure)
  (ess-switch-to-inferior-or-script-buffer t)
  (ess-rdired)
  (when (derived-mode-p 'prog-mode)
    (copilot-mode)))

(advice-add 'org-edit-src-code
            :before (defun sloth/org-edit-src-code/before (&rest args)
                      (when-let* ((element (org-element-at-point))
                                  (type (org-element-type element))
                                  (lang (org-element-property :language element))
                                  (mode (org-src-get-lang-mode lang))
                                  ((eglot--lookup-mode mode))
                                  (edit-pre (intern
                                             (format "org-babel-edit-prep:%s" lang))))
                        (if (fboundp edit-pre)
                            (advice-add edit-pre :after #'sloth/org-babel-edit-prep)
                          (fset edit-pre #'sloth/org-babel-edit-prep)))))

Support for the Nim language

(use-package nim-mode
    :ensure t)

;; --- Tree-sitter for Nim ---------------------------------------------
;; Install the Nim grammar first:
;;   M-x treesit-install-language-grammar RET nim RET
;; Then use community nim-ts-mode:
(use-package nim-ts-mode
  :vc (:url "https://github.com/niontrix/nim-ts-mode" :rev :newest)
  :mode ("\\.nim\\'" . nim-ts-mode)
  :init
  (add-to-list 'treesit-language-source-alist
               '(nim "https://github.com/alaviss/tree-sitter-nim")))

(defun nimble-build-if-project ()
  "Check if the current project is a Nimble project and run 'nimble build' from the project root."
  (interactive)
  (let ((project-root (locate-dominating-file
                       default-directory
                       (lambda (dir)
                         (directory-files dir nil "\\.nimble\\'")))))
    (if project-root
        (progn
          (message "Project root found: %s" project-root)
          (let ((default-directory project-root))
            (message "Running 'nimble build' in directory: %s" default-directory)
            (compile "nimble build")))
      (message "Not a Nimble project: No '*.nimble' file found in the project root."))))

(with-eval-after-load 'nim-mode
  (define-key nim-mode-map (kbd "C-c C-c") 'nimble-build-if-project))

Miscellaneous utilities

;(use-package ultra-scroll
  ;:load-path "~/code/emacs/ultra-scroll" ; if you git clone'd instead of package-vc-install
;  :init
;  (setq scroll-conservatively 101 ; important!
;        scroll-margin 0)
;  :config
;  (ultra-scroll-mode 1))

(require 'notifications)
(defun notify-on-command-end ()
  "Send a desktop notification when an ESS command ends."
  (interactive)
  (notifications-notify
   :title "ESS Command Finished"
   :body "Your ESS command has completed."))

(defun my-ess-eval-advice (orig-fun &rest args)
  "Advice to add a notification after `ess-eval-linewise'."
  (apply orig-fun args)
  (notify-on-command-end))

(advice-add 'org-babel-execute-src-block :around #'my-ess-eval-advice)
(advice-add 'ess-eval-region :around #'my-ess-eval-advice)
(advice-add 'ess-eval-buffer :around #'my-ess-eval-advice)
(advice-add 'ess-eval-line :around #'my-ess-eval-advice)
(advice-add 'ess-eval-line-and-step :around #'my-ess-eval-advice)
(advice-add 'ess-eval-function :around #'my-ess-eval-advice)
(advice-add 'ess-eval-linewise :around #'my-ess-eval-advice)


(customize-set-variable
 'tramp-ssh-controlmaster-options
 (concat
    "-o ControlPath=/tmp/ssh-ControlPath-%%r@%%h:%%p "
    "-o ControlMaster=auto -o ControlPersist=yes"))

;;(setq tramp-verbose 6)

(add-hook 'emacs-startup-hook
          (lambda ()
            (message "Emacs loaded in %.2fs with %d GCs"
                     (float-time (time-subtract after-init-time before-init-time))
                     gcs-done)))

(setq recentf-max-saved-items 100)

About

Single-file Emacs configuration for a powerful scientific Notebook system that works flawlessly over ssh.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors