Skip to content

Latest commit

 

History

History
191 lines (141 loc) · 7.92 KB

README.org

File metadata and controls

191 lines (141 loc) · 7.92 KB

ECFG: Emacs Config

Foreword

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.

Installation

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 "$@" & }

How it works

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.

El-Get

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 macro ecfg-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)))

Package installation policy

The new package installacion should be accompanied by the following reasoning:

  1. Does the package belong to the core configuration (will go into init.d) or it is relevant only to the specific context (modules)?
  2. 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 in el-get-recipes directory.
  3. 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 your ecfg-install. If there are multiple entry points to the package (like with helm), it would be sensible to read loaddefs for the whole package during the init. The usage of package-isolated autoloads is facilitated by ecfg-with-local-autoloads macro.

Known issues

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 to el-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 also el-get-use-autoloads customizable variable.