I started using Emacs long ago. As usually it is, everything began with having a
single ~/.emacs
file and pasting into it some random code snippets found
across the web.
After discovering some bulky plugins the ~/.emacs.d
was discovered and
separate plugin files and directories moved there. But the glue code grew, some
own functions were developed, and managing a single ~/.emacs
became awkward.
Even after splitting the initialization code into separete modules, it was still
a long time having the ~/.emacs.d
in dropbox shared across multiple
workstations and containing all the necessary plugins cherry-picked from the
net.
The major problem was the unmaintainability of these plugin
versions. Approximately at this time the package.el
grew its popularity and
the whole stuff started looking promising. Another reason to move towards
network-based package deployment was my wish to switch from the dropbox solution
to github because at my new working place firewall didn’t allow dropbox
access: Having all the third-party plugins in git looked a bit unwieldy.
The tipping point came when I discovered an amazing
Oh My Emacs. It proved the concept
of maintaining an extremely elaborate Emacs configuration in a transparent and
manageable way. And though I prefer to keep my config looking like code and not
like the document (still, I am totally a fan of the org-mode
), the ome
has
heavily influenced the current state of things.
Currently, neither the ~/.emacs
file, nor ~/.emacs.d
are not replaced by the
config. The idea behind that is to keep the configuration sources as far as
possible from automatic updates from the Emacs and its plugins side.
All the artifacts created by different emacs modes (i.e. recentf
history) are
encouraged to be stored in ~/.emacs.d
, all the user-specific settings (like
custom-set-variables
stuff set by emacs configuration tool) are encouraged to
stay in the ~/.emacs
.
Anyways, there is always a possibility to experiment with user-emacs-directory
and with custom-file
.
To start using the ecfg
, clone the repo into ~/.emacs.config
or anywhere you
find more appropriate.
git clone https://github.com/maslennikov/emacs-config.git ~/.emacs.config
To load the ecfg
during the Emacs start-up, put as a first line into your
~/.emacs
the following:
;; in case you use windows, teach it the utf8
(prefer-coding-system 'utf-8)
(load-file "~/.emacs.config/init.el")
;; for those preferring to have the emacs wingow maximized
(add-to-list 'default-frame-alist '(fullscreen . maximized))
It can take a few minutes, all depending on your internet connection speed, for the initial start-up to download and set all the required packages.
When mainly starting the emacs workflow from shell, it could be helpful to have
the following function added to the ~/.bashrc
em() { emacs -mm "$@" & }
The entry point to the ecfg
is the init.el
file. It takes care of the
correct load order of the rest modules.
Configuration files are located in two directories:
init.d
: contains core emacs configuration, universally applied to all emacs activities;modules
: contains configuration relevant for particular contexts (e.g. major mode set-ups).
Every of this files is called a “module” in terms of ecfg
. For each module, a
init function with a special name can be defined: ecfg-NAME-module-init
, where
the module’s name is acquired from its filename of the format
ecfg-NAME.el
. After loading the module, its init function will be called, if
present.
Core configuration from the init.d
is loaded immediately during the emacs
startup, therefore a careful analysis of potential performance implications is
required when modifying these files. Please refer to the folliwing “Package
installation policy” section.
Configuration from the modules
directory is handled with the autoloads
facility. During the first run, the autoloads for the whole directory will be
generated (see ecfg-user-dir
) and will be used in the subsequent runs. It is
recommended to have the module’s init function autoloaded and to register this
function as a hook in auto-mode-alist
via the helper macro ecfg-auto-module
.
Example:
;;;###autoload (ecfg-auto-module "CMakeLists\\.txt$" cmake)
In this case, the whole module will be forced to be loaded only when a file with the corresponding pattern is opened.
All third-party packages are installed via el-get, therefore it is bootstrapped
as the first step. Instead of using el-get
directly, the ecfg-install
macro
should be used.
The standard el-get
behavior is restricted for performance reasons to just
downloading the packages and adding their directories to the
load-path
:
- Package won’t be automatically ‘require’d.
- Automatic autoloads management is disabled; package init hooks are passed
dynamically via the
ecfg-install
macro.In case when autoloads should be generated and loaded for the whole package (normally, when a manual
autoload
invocation in the init hook is not convenient to use), the macroecfg-with-local-autoloads
can be used. - Nested
ecfg-install
invocations are posible to manage package dependencies.
To override the el-get
recipes, store the fixed recipe file in
el-get-recipes
directory.
Example of package installations (see also Package installation policy):
;; installing the `drag-stuff' package: not requiring it immediately, explicitly
;; defining autoloads without building package loaddefs.el.
(ecfg-install drag-stuff
(autoload 'drag-stuff-up "drag-stuff")
(autoload 'drag-stuff-down "drag-stuff"))
;; installing `ido-ubiquitous' package: requiring it and using immediately after
;; installation
(ecfg-install ido-ubiquitous
(require 'ido-ubiquitous)
(ido-ubiquitous-mode))
;; this is where package-local autoloads come handy: not requiring `projectile'
;; immediately but setting shortcut hooks to lazy load the package on demand
(ecfg-install projectile
(ecfg-with-local-autoloads
(global-set-key (kbd "C-s-f") 'helm-projectile-find-file-dwim)
(global-set-key (kbd "<f8>") 'helm-projectile-find-other-file)
(global-set-key (kbd "<f9>") 'helm-projectile-grep)))
The new package installacion should be accompanied by the following reasoning:
- Does the package belong to the core configuration (will go into
init.d
) or it is relevant only to the specific context (modules
)? - Is there anything in
el-get
recipe that will cause an immediate loading of the package (e.g.:features
or specific:prepare
code)? If yes, override the recipe with the local copy inel-get-recipes
directory. - What event will trigger the package load? For instance, if it is always the
invocation of a single command (e.g. via s shortcut), then it’ll make sense
to write a single
(autoload ...)
statement in the init hook of yourecfg-install
. If there are multiple entry points to the package (like withhelm
), it would be sensible to readloaddefs
for the whole package during the init. The usage of package-isolated autoloads is facilitated byecfg-with-local-autoloads
macro.
After updating your modules
, remember to remove ecfg-user-dir/loaddefs.el
.
While experimenting with el-get
, el-get-bundle
, and recipes, be sure to
remember about the cached stuff:
el-get/bundle-init/
: contains passed toel-get-bundle
init code;el-get/.status
: contains cached recipes for all installed packages;el-get/.loaddefs.el
: contains autoloads from all installed packages - can be large. See alsoel-get-use-autoloads
customizable variable.