Trim Emacs config to a lean editor; fix cold-start failures - #27
Merged
Conversation
Startup drops from ~1.43s to ~0.18s wall clock (init-time 0.29s -> 0.13s, 7 GCs -> 0), and the package set from 41 to 18. - Add early-init.el: GC deferral, frame chrome as frame parameters, and native-comp warning suppression, all before the first frame is drawn. - Refresh package archives once up front and install compat before anything macro-expands against it. A first run against an empty elpa/ previously died with "Cannot open load file: compat-31". - Drop the IDE layer: lsp-mode, lsp-ui, lsp-treemacs, treesit-auto, and the rust/typescript/deno modes. treesit-auto remapped every language to a *-ts-mode while only bash, toml and yaml grammars were installed, so JSON threw "Tree-sitter for JSON isn't available" on every startup. - Drop desktop-save-mode, the single largest startup cost, which also restored file paths that don't exist on other machines. save-place and recentf cover the useful part for near-zero cost. - Drop ido, which was fighting vertico over the same minibuffer reads, and the which-key package, which is built in as of Emacs 30. - Move font, theme and modeline-icon setup to a per-frame hook. $EDITOR is emacsclient, and the daemon has no display when init.el is read, so the old display-graphic-p checks answered for the wrong frame. - Remove the gnutls TLS 1.3 workaround, an Emacs 26-era setting that was disabling TLS 1.3 on a 30.2 build. - Fix major-mode to use setq-default, scope show-trailing-whitespace to prog/text/conf modes, load custom-file, and dedupe the repeated column-number-mode, xterm-mouse-mode and before-save-hook entries. - Add markdown-mode explicitly; it previously arrived as an lsp-mode dependency.
The derived path was $TMPDIR/emacs/server on macOS, but Emacs 30 uses $TMPDIR/user/<uid>/emacs. The `e` alias therefore never reached a running server and cold-started a fresh Emacs on every invocation. emacsclient derives the same default socket path that server-start writes, so drop both the derivation and the --socket-name flag rather than keep a second copy of that logic in sync.
tlockney
marked this pull request as ready for review
July 27, 2026 18:12
The ShellCheck job has never passed, on main or on any pull request. All 14 findings are info-level: 13 are SC1091 "Not following: lib/common.sh", raised because bin/ scripts source it through a SCRIPT_DIR computed at runtime, and one is an SC2016 on a line that deliberately prints a literal eval command for the reader to copy. `shellcheck -x -P SCRIPTDIR` resolves all 13 properly, but action-shellcheck@2.0.0 declares an `options` input and never references it, so extra flags cannot be passed at this version. Setting severity to warning is the available lever, and it matches how bin/lint-shell already treats these findings: it runs shellcheck with `|| true` and never fails. Real defects at warning and error level still fail the build.
This was referenced Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reduces the Emacs config to a lean text editor and fixes the errors that made a first run on a new machine fail.
emacs -nw, 3 runs)emacs-init-timeCannot open load file: compat-31Changes
New
.emacs.d/early-init.el— GC deferral, frame chrome as frame parameters,native-comp-async-report-warnings-errors 'silent, andfile-name-handler-alistsuppression during startup.Removed (~25 packages, including the whole treemacs/hydra/avy/posframe subtree):
lsp-mode,lsp-ui,lsp-treemacs,treesit-auto,deno-ts-mode,typescript-mode,typescript-ts-mode,rust-mode,writeroom-mode,ido-completing-read+, thewhich-keypackage (built into Emacs 30), anddesktop-save-mode.Bugs fixed
compatinstalls before anything macro-expands against it.treesit-autoremapped every language to a*-ts-modewhile only bash/toml/yaml grammars existed, throwingTree-sitter for JSON isn't availableon every startup.ido-everywhereandvertico-modewere both enabled, competing for the same minibuffer reads.gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"was disabling TLS 1.3 on an Emacs 30.2 build.(setq major-mode 'text-mode)→setq-default.$EDITORisemacsclientand the daemon has no display wheninit.elis read.custom-filewas set but never loaded.column-number-mode,xterm-mouse-mode, and two overlappingbefore-save-hookentries.show-trailing-whitespacescoped to prog/text/conf modes.Separately:
$EMACS_SOCKET_NAME— the shell derived$TMPDIR/emacs/server, but Emacs 30 uses$TMPDIR/user/<uid>/emacs/server. Theealias never reached the server and cold-started a new Emacs every time. Both the derivation and the--socket-nameflag are removed;emacsclientcomputes the same default the server writes.Verification
--init-directorywith a real network fetch: exit 0, no runtime errors, 18 packages.completion-stylesis(orderless basic), and recentf keeps the 1000-item limit and.recentffile.doom-modeline-iconresolves tonilandcorfu-terminal-modetot.<escape>binding confirmed safe in a terminal:ESC xstill runsexecute-extended-command,[27]is stillESC-prefix.zsh -n.Notes
material-themeis unmaintained (2015) and specifies some nil face attributes, which Emacs 30 warns about into*Messages*on a graphical frame. It's the only remaining startup noise; a comment ininit.elnotes that(load-theme 'modus-vivendi)is a built-in, warning-free swap. Left as-is rather than change the theme unasked.yadm merge working-branchfrom the home directory, not a GitHub merge. Merging this PR is an alternative path — don't do both without pulling first.CI note (third commit)
The ShellCheck job was already failing before this PR — on
mainand on every pull request going back to at least November 2025. This PR merely triggered it, because the workflow'spaths:filter includes.config/zsh/**.None of the files changed here are scanned: the job uses
scandir: './bin'plus two named.shfiles, and shellcheck cannot parse zsh regardless. Reproduced locally — 14 findings, all in./bin, all info-level: 13 × SC1091 (Not following: lib/common.sh, because those scripts source it via a runtime-computedSCRIPT_DIR) and 1 × SC2016 on a line that intentionally prints a literalevalfor the reader to copy.shellcheck -x -P SCRIPTDIRresolves all 13 legitimately (verified locally: 14 findings → 1), butaction-shellcheck@2.0.0declares anoptionsinput and never references it, so extra flags can't be passed at that version. Settingseverity: warningis the available lever, and it matches howbin/lint-shellalready treats these findings — it runs shellcheck with|| trueand never fails. Warning- and error-level defects still fail the build.This commit is deliberately separate so it can be dropped or split into its own PR if you'd rather fix the lint posture independently.