Publi

Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (I – Configuración general)

Emacs ha sido mi editor de texto preferido durante más de 10 años. Y, aunque casi desde el principio (desde que empecé a usarlo en serio) quise personalizarlo con
las opciones que más o menos consideraba interesantes, esa configuración ha estado durante muchos años abandonada. Funcionando bien, pero abandonada. Y, sin aprovechar muchas de las nuevas características y extensiones que están disponibles.

El año pasado, me animé a publicar mi .emacs.d en GitHub. Con muchas novedades, aunque no me terminaba de convencer al 100% y, como estas cosas pueden ser un tanto duras (reconozco que una configuración de Emacs desde cero puede desanimar a cualquiera), he decidido hacer estos pequeños apuntes. Para conseguir una configuración sin calentarnos la cabeza en exceso, pero con cierto detalle, para que todos podamos modificarla y personalizarla a nuestro gusto.

Nota: El post original de la configuración de Emacs se me ha alargado demasiado, aunque incluye mucho código. Así que lo he dividido en varias partes que iré publicando estos días e incluiré un resumen para digerir todo esto de forma sencilla.

En este post sobre la configuración general de Emacs me voy a centrar en la descripción general de todo lo que quiero configurar y aspectos como la ventana, la modeline, el gestor de paquetes y de código externo, ventanas y algunos modos generales. Además, incluyo agradecimientos a muchos de los desarrolladores que hacen esto posible (recordemos que aunque sea una configuración, en Emacs todo viene programado en Emacs Lisp). También cabe decir que las últimas versiones de Emacs compilan el código Lisp para agilizar su ejecución, por lo que no tenemos que obsesionarnos con la pérdida de velocidad de un lenguaje interpretado.

Antes de empezar

Hay ciertas extensiones que, aunque son tremendamente buenas, no las he incluido en mi configuración porque realmente no las uso, o tengo otras alternativas. Aunque Emacs puede convertirse incluso en nuestro gestor de ventanas, para algunas acciones, como el correo, lector RSS, notas, etc utilizo programas externos que me resultan más amigables. Así que, aunque muchos usuarios de Emacs las aman con locura, no incorporaré:

GitHub

En unos días me gustaría publicar la configuración completa en mi GitHub. Aún tengo que retocar algunas partes y poner algunos comentarios para que quede completa. A partir de ese momento, la última versión de la configuración estará en esa web. Desde aquí se podrá seguir consultando la guía y alguna información importante, sobre todo el desarrollo y las explicaciones, pero tal vez en el futuro vayan cambiando algunas cosas o vaya variando de forma fina ciertos aspectos, o incluyendo algún nuevo modo y algunas cosas no las actualizaré aquí.

Probando configuraciones

Como compartí en un post anterior sobre Emacs, la configuración de Emacs suele ser por usuario. Eso imposibilita poder probar varias configuraciones del editor en la misma sesión de usuario. Aunque podemos utilizar este pequeño código y pegarlo en nuestro ~/.emacs:

1
2
3
4
5
6
(defvar user-custom-dir (getenv "EMACS_USER_DIR"))

(when (/= (length user-custom-dir) 0)
  (setq user-emacs-directory (file-name-as-directory user-custom-dir)))

(load (expand-file-name "init.el" user-emacs-directory))

De esta forma, el fichero de configuración que se utilice será ~/.emacs y, a partir de ahí, atendiendo al valor de la variable de entorno EMACS_USER_DIR podremos cargar la configuración que queramos. Si nuestra configuración la estamos probando, por ejemplo en el directorio ~/.emacs.d_new podemos hacer:

EMACS_USER_DIR=~/.emacs.d_new emacs

Y se ejecutará el programa con la configuración deseada.

Agradecimiento a muchos grandes desarrolladores

Mi configuración no sería posible sin los miles de personas alrededor del mundo que han hecho posible GNU Emacs (M-x about-emacs), todos los que han desarrollado plugins (iré detallando autores) y a todos los que han publicado sus .emacs.d por Internet y de los cuales he ido cogiendo algunos recortes. En este post no soy el autor de muchas de las funciones ni los trozos de código que hay publicados, aunque sí que los he probado todos. También he desarrollado algunas partes, y me he basado en el trabajo de otros para completar algunas funcionalidades. Especialmente deberían funcionar a partir de Emacs 24.5 que, aunque no es el último (vamos por Emacs 25.3 a día de hoy), es el que muchas distribuciones traen en sus repositorios. De todas formas, también debe ser compatible con la última versión de Emacs.

En estos ficheros incluiré líneas de código de:

Archivos y directorios: organización

Debemos recordar que la configuración podemos complicarla un montón. Y, en este directorio, además de configuración, irán los ficheros de los módulos, extensiones y temas que queramos utilizar. Por otro lado podremos también encontrar archivos de caché, diccionarios, colecciones de código para autorrellenar, layouts guardadas, etc.
Emacs, también tiene varias formas de incluir módulos:

  • Desde el SO. Es decir, podemos instalar los módulos desde el repositorio de nuestro sistema operativo. Y estos módulos serán a nivel de sistema afectando a todos los usuarios. De todas formas, en la actualidad cada vez se usa menos esta forma de hacer las cosas. Lo suyo es hacerlo desde la configuración de Emacs. Él se encarga de las actualizaciones, incluso puede ser que una versión del plugin no nos funcione y debamos imponer una anterior para una configuración, o que nuestra distribución no nos dé la última, y si nos llevamos la configuración de un ordenador a otro, no funcione.
  • Desde los repositorios de Emacs. Emacs tiene su propio gestor de paquetes, muy chulo, muy estilo Emacs. Y, muchas extensiones las utilizaremos de esta manera. Es más, si instalas una de las configuraciones que alguien ha subido a Internet, la primera vez, Emacs tardará muchísimo en arrancar porque tendrá que descargar y compilar todos los módulos que utilicemos.
  • Copiados directamente en .emacs.d. Todas las extensiones del mundo, no están en el repositorio. O tal vez, la versión que nos interesa. Así que podemos copiar todos los archivos .el del módulo dentro de nuestro .emacs.d

Lo bueno es que si cambiamos de ordenador, podemos llevarnos toda la configuración, junto con módulos y demás a cualquier lado.

Básicamente vamos a tener esta estructura. Aunque se irán añadiendo algunos directorios y archivos automáticamente para facilitarnos las cosas:

.
├── custom.el : Variables personalizadas. Algunos programas suelen escribir aquí, mejor no lo toquemos.
├── elpa : Directorio donde se almacenan todos los paquetes descargados de Emacs
├── init.el : Fichero principal de configuración
├── lisp : Ficheros que se llaman directamente desde el fichero de configuración
│   └── lib : Bibliotecas que usarán los diferentes archivos de configuracíón
├── semanticdb : Base de datos de ficheros, bibliotecas, etc para autocompletado
├── settings.el : Variables de configuración personales
├── site-lisp : Módulos LISP que no están en el gestor de paquetes y debemos añadir a mano

Características que necesito

Aunque, mientras creamos la configuración vamos a descubrir cosas que están muy bien, lo primordial, para mí, es lo siguiente:

  • Lenguajes preferidos: C, C++, Bash, Python, PHP, SQL, HTML, CSS, Javascript. Son lenguajes con los que voy a pasar mucho tiempo y me interesa que todo funcionen a las mil maravillas y estén personalizados. Tanto el coloreado como la indentación. Además, a veces tendré que utilizar varias indentaciones diferentes dependiendo del proyecto. Ya sabéis, hay que adaptarse a las reglas de estilo de cada desarrollo.
  • Otros lenguajes: SCSS, Ruby, XML, Java. No los suelo usar mucho y me apaño con lo básico.
  • Números de línea en el lateral izquierdo. Como casi todos los editores.
  • Auto completado de código. Aunque Emacs tiene muy buenas funciones de autocompletado con la combinación M-/, en ocasiones necesitamos un autocompletado de código más inteligente, basado en el propio lenguaje y el código que estamos escribiendo, capaz de escanear variables y clases para delimitar de forma más concreta lo que vamos a escribir y ahorrar tiempo. Yo utilizaré auto-completion-mode y semantic.

Empezamos init.el

Vamos a empezar a picar nuestro init.el . Aunque básicamente lo que vamos a hacer es incluir cosas. De todas formas, al principio y al final hacemos algunas cosas interesantes como:

  • Definir la ruta desde la que cargará archivos: lisp/
  • Activar un cronómetro que nos dirá cuánto tarda Emacs en arrancar y nos permitirá afinar la configuración o ver si hay algún módulo que está metiendo la pata en el arranque.
  • Definir el garbage collection cuando haya terminado. Esto lo podríamos meter en una función si queremos.
  • Cargar archivos settings.el y custom.el
  • Incluir muchas configuraciones individuales.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
;;; Emacs configuration bootstrap file. This file will call a number
;;; of files, located first in lisp/

(when (version<= emacs-version "24")
  (error "This is made form Emacs >=24"))

(defconst emacs-start-time (current-time))
(add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory))

;;; Raise garbage collection threshold after init
(add-hook 'after-init-hook
      (lambda () (setq gc-cons-threshold local/gc-cons-threshold)))

;;; Custom variables
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
;;; Custom settings
(setq settings-file (expand-file-name "settings.el" user-emacs-directory))

;;; Loads settings file
(when (file-exists-p custom-file)
  (load settings-file))

;; Require everything...
(require 'init-utils)
...
...
...

;;; Loads custom file
(when (file-exists-p custom-file)
  (load custom-file))

(when window-system
  (let ((elapsed (float-time (time-subtract (current-time)
                                            emacs-start-time))))
    (message "[STARTUP] Loading %s ... done (%.3fs)" load-file-name elapsed)))

(provide 'init)

En medio del fichero dice “;;; Require everything” pongo un require y puntos suspensivos. Eso es porque vamos a requerir cada uno de los ficheros que pongamos ahí dentro de lisp/ . Por supuesto que podemos poner todo en un archivo, y que éste sea tremendamente grande, pero vamos a hacer las cosas bien y vamos a dejarlo curioso. Cada parte que queramos inicializar la escribiremos en un archivo. Si es el tema, init-theme, si es la ventana init-window, si es un conjunto de herramientas para python, init-python, y así sucesivamente. Y dentro de cada uno, puede haber más requires.

Con esto nos acostumbramos un poco a Lisp. Además, vemos que al final del fichero ponemos (provide ‘init). Eso lo haremos al final de cada fichero, proporcionando un nombre adecuado en cada archivo (todos no van a ser init).

Utilidades y gestor de paquetes


Aquí incluiremos cosas generales, en general son ficheros pequeños, incluiremos funciones de bibliotecas (las bibliotecas las pondré al final) y prepararemos el gestor de paquetes de Emacs para que automáticamente descargue e instale paquetes que necesitamos y no estén instalados.
Además, aunque intentaremos tirar de gestor de paquetes para todo, así tendremos actualizaciones automáticas y tendremos al día muchas de las utilidades de Emacs; algunas no están presentes en este gestor, o puede que necesitemos versiones diferentes para nuestro sistema. Tenemos que tener en cuenta que si tenemos una versión de Emacs algo antigua, habrá paquetes del gestor de paquetes que sean demasiado nuevos para nosotros, así que tendremos que incluir una versión algo más antigua. Esto lo podemos hacer copiándolo a site-lisp/

init-utils.el

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;; Loads functions from libs
(defun load-directory (dir)
  (let ((load-it (lambda (f)
           (load-file (concat (file-name-as-directory dir) f)))
         ))
    (mapc load-it (directory-files dir nil "\\.el$"))))

;; Load lib functions
(load-directory (expand-file-name "lisp/lib/" user-emacs-directory))

;; This is borrowed from https://github.com/purcell/emacs.d/blob/master/lisp/init-utils.el by Steve Purcell but I have added some stuff.

(if (fboundp 'with-eval-after-load)
    (defalias 'after-load 'with-eval-after-load)
  (defmacro after-load (feature &rest body)
    "After FEATURE is loaded, evaluate BODY."
    (declare (indent defun))
    `(eval-after-load ,feature
       '(progn ,@body))))

;; Elapsed time
(myemacs/elapsed-time)
(provide 'init-utils)

init-site-lisp.el

Nos ayuda a cargar los ficheros que hay en site-lisp sin trabajar mucho.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
;;; Set load path

(eval-when-compile (require 'cl))
(defun sanityinc/add-subdirs-to-load-path (parent-dir)
  "Adds every non-hidden subdir of PARENT-DIR to `load-path'."
  (let* ((default-directory parent-dir))
    (progn
      (setq load-path
            (append
             (remove-if-not
              (lambda (dir) (file-directory-p dir))
              (directory-files (expand-file-name parent-dir) t "^[^\\.]"))
             load-path)))))

;; Create site-lisp directory if not exists
(let ((dir (expand-file-name "site-lisp/" user-emacs-directory)))
  (unless (file-exists-p dir)
    (make-directory dir)))

(sanityinc/add-subdirs-to-load-path
 (expand-file-name "site-lisp/" user-emacs-directory))

;;; Utilities for grabbing upstream libs

(defun site-lisp-dir-for (name)
  (expand-file-name (format "site-lisp/%s" name) user-emacs-directory))

(defun site-lisp-library-el-path (name)
  (expand-file-name (format "%s.el" name) (site-lisp-dir-for name)))

(defun download-site-lisp-module (name url)
  (let ((dir (site-lisp-dir-for name)))
    (message "Downloading %s from %s" name url)
    (unless (file-directory-p dir)
      (make-directory dir t))
    (add-to-list 'load-path dir)
    (let ((el-file (site-lisp-library-el-path name)))
      (url-copy-file url el-file t nil)
      el-file)))

(defun ensure-lib-from-url (name url)
  (unless (site-lisp-library-loadable-p name)
    (byte-compile-file (download-site-lisp-module name url))))

(defun site-lisp-library-loadable-p (name)
  "Return whether or not the library `name' can be loaded from a
source file under ~/.emacs.d/site-lisp/name/"

  (let ((f (locate-library (symbol-name name))))
    (and f (string-prefix-p (file-name-as-directory (site-lisp-dir-for name)) f))))

(myemacs/elapsed-time)
(provide 'init-site-lisp)

init-elpa.el

Inicializa el sistema de paquetes ELPA (Emacs Lisp Package Archive) y la instalación bajo demanda de paquetes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
;;; Original file: https://github.com/purcell/emacs.d/blob/master/lisp/init-benchmarking.el by Steve Purcell

;;; Find and load the correct package.el

;; When switching between Emacs 23 and 24, we always use the bundled package.el in Emacs 24
(let ((package-el-site-lisp-dir
       (expand-file-name "site-lisp/package" user-emacs-directory)))
  (when (and (file-directory-p package-el-site-lisp-dir)
             (> emacs-major-version 23))
    (message "Removing local package.el from load-path to avoid shadowing bundled version")
    (setq load-path (remove package-el-site-lisp-dir load-path))))

(require 'package)

;;; Standard package repositories

(when (< emacs-major-version 24)
  ;; Mainly for ruby-mode
  (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/")))

;; We include the org repository for completeness, but don't normally
;; use it.
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/"))

(when (< emacs-major-version 24)
  (add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/")))

;;; Also use Melpa for most packages
(add-to-list 'package-archives `("melpa" . "https://melpa.org/packages/"))

(sanityinc/package-maybe-enable-signatures)
(after-load 'init-exec-path
  (sanityinc/package-maybe-enable-signatures))


;;; On-demand installation of packages
(defun require-package (package &optional min-version no-refresh)
  "Install given PACKAGE, optionally requiring MIN-VERSION.
If NO-REFRESH is non-nil, the available package lists will not be
re-downloaded in order to locate PACKAGE."

  (if (package-installed-p package min-version)
      t
    (if (or (assoc package package-archive-contents) no-refresh)
        (if (boundp 'package-selected-packages)
            ;; Record this as a package the user installed explicitly
            (package-install package nil)
          (package-install package))
      (progn
        (package-refresh-contents)
        (require-package package min-version t)))))

(defun maybe-require-package (package &optional min-version no-refresh)
  "Try to install PACKAGE, and return non-nil if successful.
In the event of failure, return nil and print a warning message.
Optionally require MIN-VERSION.  If NO-REFRESH is non-nil, the
available package lists will not be re-downloaded in order to
locate PACKAGE."

  (condition-case err
      (require-package package min-version no-refresh)
    (error
     (message "Couldn't install package `%s': %S" package err)
     nil)))

;;; Fire up package.el

(setq package-enable-at-startup nil)
(package-initialize)


(require-package 'fullframe)
(fullframe list-packages quit-window)

(require-package 'cl-lib)
(require 'cl-lib)

(add-hook 'package-menu-mode-hook 'sanityinc/maybe-widen-package-menu-columns)

(myemacs/elapsed-time)
(provide 'init-elpa)

Configuración del tema: init-theme.el

Antes usaba otro tema, que se puede ver comentado. Ahora utilizo color-theme, de nuevo, con el que tenemos un montón de temas de color muy chulos para elegir. Además, introduzco definiciones de color para linum (que lo veremos después), ya que el color de resaltado de línea del tema no me termina de convencer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
;(require-package 'hc-zenburn-theme)
(require-package 'color-theme-modern)       ; color-theme with Emacs24 framework

;(load-theme 'hc-zenburn)
(load-theme 'charcoal-black t)      ; last t is NO_CONFIRM param

; Fix linum current-line highlight. Doesn't looks good with this theme
(defface my-linum-hl
  `((t :background "gray30" :foreground "gold"))
  "Face for the currently active Line number"
  :group 'linum)

(myemacs/elapsed-time)
(provide 'init-theme)

Configuración del entorno gráfico GUI (init-gui.el)

¿Cómo quiero el entorno gráficao de Emacs? No lo explico todo, pero sí algunas cosas interesantes:

  • No utilizo diálogos del entorno gráfico. Navego por los archivos desde los buffers de Emacs y en lugar de diálogos uso el minibuffer. Por eso, use-file-dialog y use-dialog-box los pongo a nil.
  • Personalizo el título de la ventana
  • Desactivo también tool-bar-mode para la barra de herramientas, menu-bar-mode para los menús y set-scroll-bar-mode para la barra de scroll
  • Uso linum-mode para mostrar los números de línea y hl-line para resaltar la línea actual (parte de la configuración extraída de aquí)
  • No me gusta que Control+Z minimice la ventana. Muchas veces lo pulso por equivocación
  • Cuando hay algún error, o se acaba el autocompletado, o los buffers llegan a su fin, Emacs, hace sonar la campana, o el altavoz interno del ordenador. Se puede vincular a un sonido del sistema de error, pero muchas veces mientras editamos suena demasiado. Yo prefiero tener una señal visible y sutil (invierto los colores de parte de la mode-line, es el visible-bell.
  • Defino algunas teclas personalizadas (vemos más adelante el resumen de teclas)
  • Escribe fecha y hora en la modeline
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
;;----------------------------------------------------------------------------
;; Remove some GUI stuff
;;----------------------------------------------------------------------------
(setq use-file-dialog nil)
(setq use-dialog-box nil)
(setq inhibit-startup-screen t)
(setq inhibit-startup-echo-area-message t)
(tool-bar-mode -1)
(set-scroll-bar-mode nil)
(menu-bar-mode 0)

;;----------------------------------------------------------------------------
;; Editor configuration
;;----------------------------------------------------------------------------
(setq indicate-empty-lines t)

(let ((no-border '(internal-border-width . 0)))
  (add-to-list 'default-frame-alist no-border)
  (add-to-list 'initial-frame-alist no-border))

(setq frame-title-format
      '((:eval (if (buffer-file-name)
                   (abbreviate-file-name (buffer-file-name))
                 "%b"))))
;; Non-zero values for `line-spacing' can mess up ansi-term and co,
;; so we zero it explicitly in those cases.
(add-hook 'term-mode-hook
          (lambda ()
            (setq line-spacing 0)))

;;----------------------------------------------------------------------------
;; Line numbers
;;----------------------------------------------------------------------------
;; Linum snippets from: https://www.emacswiki.org/emacs/LineNumbers
(require 'linum)
(require 'hl-line)

(defface my-linum-hl
  `((t :inherit linum :background ,(face-background 'hl-line nil t)))
  "Face for the current line number."
  :group 'linum)
(add-hook 'linum-before-numbering-hook 'my-linum-get-format-string)

(defun my-linum-get-format-string ()
  (let* ((width (1+ (length (number-to-string
                             (count-lines (point-min) (point-max))))))
         (format (concat "%" (number-to-string width) "d \u2502")))
    (setq my-linum-format-string format)))

(defvar my-linum-current-line-number 0)

(defun my-linum-format (line-number)
  (propertize (format my-linum-format-string line-number) 'face
              (if (eq line-number my-linum-current-line-number)
                  'my-linum-hl
                'linum)))
(setq linum-format 'my-linum-format)

(defadvice linum-update (around my-linum-update)
  (let ((my-linum-current-line-number (line-number-at-pos)))
    ad-do-it))
(ad-activate 'linum-update)

(defvar *linum-mdown-line* nil)

(defun line-at-click ()
  (save-excursion
    (let ((click-y (cdr (cdr (mouse-position))))
          (line-move-visual-store line-move-visual))
      (setq line-move-visual t)
      (goto-char (window-start))
      (next-line (1- click-y))
      (setq line-move-visual line-move-visual-store)
      ;; If you are using tabbar substitute the next line with
      ;; (line-number-at-pos))))
      (1+ (line-number-at-pos)))))

(defun md-select-linum ()
  (interactive)
  (goto-line (line-at-click))
  (set-mark (point))
  (setq *linum-mdown-line*
        (line-number-at-pos)))

(defun mu-select-linum ()
  (interactive)
  (when *linum-mdown-line*
    (let (mu-line)
      ;; (goto-line (line-at-click))
      (setq mu-line (line-at-click))
      (goto-line (max *linum-mdown-line* mu-line))
      (set-mark (line-end-position))
      (goto-line (min *linum-mdown-line* mu-line))
      (setq *linum-mdown*
            nil))))

(global-set-key (kbd "<left-margin> <down-mouse-1>") 'md-select-linum)
(global-set-key (kbd "<left-margin> <mouse-1>") 'mu-select-linum)
(global-set-key (kbd "<left-margin> <drag-mouse-1>") 'mu-select-linum)

(add-hook 'find-file-hook (lambda () (linum-mode 1)))
(linum-mode)
(setq global-linum-mode t)

;;----------------------------------------------------------------------------
;; Other settings
;;----------------------------------------------------------------------------
;; Visible bell, but flash only modeline. Thanks to http://www.grantjenks.com/
(setq visible-bell nil)
(setq ring-bell-function (lambda ()
                           (invert-face 'mode-line)
                           (run-with-timer 0.1 nil 'invert-face 'mode-line)))

;; Semi-bold and italic comments
(set-face-attribute 'font-lock-comment-face nil :slant 'italic)
(set-face-attribute 'font-lock-comment-face nil :weight 'semibold)

;;----------------------------------------------------------------------------
;; Configure keys
;;----------------------------------------------------------------------------
(global-unset-key (kbd "C-z"))      ; Stops C-z from minimizing window
(global-set-key (kbd "M-<down>") (lambda () (interactive) (sanityinc/adjust-opacity nil -2))) ; M-down less visibility
(global-set-key (kbd "M-<up>") (lambda () (interactive) (sanityinc/adjust-opacity nil 2))) ; M-up more visibility
(global-set-key (kbd "M-0") (lambda () (interactive) (modify-frame-parameters nil `((alpha . 100))))) ; M-0 standard visibility
(global-set-key (kbd "M-f") 'myemacs/toggle-fullscreen) ; M-f FullScreen
(global-set-key (kbd "s-C-+") 'sacha/increase-font-size)    ; C-+ increase font size
(global-set-key (kbd "s-C--") 'sacha/decrease-font-size)    ; C-- decrease font size
(global-set-key (kbd "<f12>") 'revert-buffer-no-confirm)
(global-set-key (kbd "s-h") 'global-hl-line-mode)   ; Highlight current line

(setq display-time-day-and-date t)
(display-time)

(myemacs/elapsed-time)
(provide 'init-gui)

Como parte de la configuración del entorno, podemos incluir la configuración de la mode-line. Aunque la he incluido aparte porque a cada uno nos gusta una mode-line, así podemos elegirla, y probar con y sin mode-line. A mí me gustó mucho la smart-mode-line, a la que le podemos poner el tema de la powerline. Además, he utilizado también anzu-mode para mostrar el número de resultados de las búsquedas en la modeline.

init-modeline.el

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
;;----------------------------------------------------------------------------
;; Modeline configuration
;;----------------------------------------------------------------------------

(require-package 'smart-mode-line)
(require-package 'smart-mode-line-powerline-theme)
(require-package 'sml-modeline)
;; Show number of occurrences when searching
(require-package 'anzu)

(setq sml/theme 'powerline)

(setq sml/no-confirm-load-theme t)
(setq sml/shorten-modes t)
;; Show EOL mode
(setq sml/show-eol t)
;; Show remote buffers
(setq sml/show-remote t)

(sml/setup)
(add-to-list 'sml/replacer-regexp-list '("^~/Proyectos/git/" ":Git:") t)
(add-to-list 'sml/replacer-regexp-list '("^~/www/" ":www:") t)

(sml-modeline-mode t)

(custom-set-variables
 '(anzu-search-threshold 1000)
 '(anzu-replace-threshold 1000)
 '(anzu-deactivate-region t)
 '(anzu-input-idle-delay 0.1)
 '(anzu-replace-to-string-separator " => "))
(global-anzu-mode +1)
(set-face-attribute 'anzu-mode-line nil
                    :foreground "yellow" :weight 'bold)
(after-load 'anzu
    (diminish 'anzu-mode))

;;----------------------------------------------------------------------------
;; Keyboard shortcuts in Anzu Mode
;;----------------------------------------------------------------------------
(global-set-key (kbd "M-%") 'anzu-query-replace)
(global-set-key (kbd "s-<SPC>") 'anzu-query-replace)

(myemacs/elapsed-time)
(provide 'init-modeline)

Miscelánea de edición: init-misc.el

Aquí incluyo algunas cosas interesantes. Aunque hay mucho comentado como neotree, flycheck, flyspell y yasnippet que ya no los uso; voy a incluir y configurar algunos plugins curiosos que están muy bien:

  • uniquify, originalmente escrito por Dick King, ¡en el 86! una extensión con más de 30 años que sigue siendo mantenida. Nos servirá para que Emacs renombre los buffers de un modo único. En este caso, cuando hay dos buffers con un archivo abierto con el mismo nombre, se añade al nombre del buffer el nombre del directorio donde se encuentra. Así no hay confusión. El comportamiento normal de Emacs es añadir <2> o <3> al archivo, por lo que es más lioso. Este plugin suele venir instalado con Emacs, aunque desactivado.
  • recent-files, nos da la posibilidad de abrir archivos recientes.
  • smex. por Cornelius Mika. Aumenta la funcionalidad de M-x haciendo que las búsquedas de órdenes y escritura de argumentos sean más intuitivas. Esta extensión reordena los comandos colocando los más utilizados primero, así nos resultará más cómodo acceder a ellos.
  • (fset ‘yes-or-no-p ‘y-or-n-p). Emacs, por defecto, en su afán por la seguridad, obliga a escribir “yes” o “no” de forma completa ante determinadas acciones consideradas peligrosas. Sobre todo cuando podemos perder información con ello. Aunque desde el punto de vista de la usabilidad es una pérdida de tiempo escribirlo completo cuando puedes pulsar y o n. De todas formas, esto debemos usarlo bajo nuestra responsabilidad, porque siempre podemos cargarnos algo y la culpa ya no será de Emacs.
  • mmm-mode, original de Michael Shulman. Nos da la posibilidad de poder utilizar varios major modes, en un mismo buffer. Los major modes suelen estar ligados a lenguajes de programación, proporcionando anidado y coloreado. Y, puede que, a veces, tengamos que editar un archivo en el que haya implicados varios lenguajes. El ejemplo más claro es cuando dentro de un archivo HTML, escribimos código Javascript y CSS, y puede que incluso PHP también. En ese caso, queremos que el código se coloree en consecuencia.
  • annotate, nos permite hacer anotaciones en el código que no se escribirán (a priori) en el mismo archivo (sino en un archivo de texto nuestro dentro del directorio de .emacs.d). Es útil cuando queremos hacer algo para que sólo lo veamos nosotros, escribir pequeñas notas al lado del código. Eso sí, este modo no es compatible con el modo fci (del que hablaremos más tarde), así que cuando se active este, desactivaremos fci.
  • htmlize, [autor] que crea archivos HTML de los archivos fuente que tenemos, o estamos editando. Conservando los estilos de anidado y coloreado de código. Muy útil para mostrar fragmentos de código a otras personas, o para incluir en documentación.
  • ido-mode, nos da muchas facilidades a la hora de abrir y guardar archivos con C-x C-f como búsquedas automáticas de archivos parecidos (por si no escribimos el nombre exacto o no estamos en la ruta correcta) y navegación inteligente por las carpetas.
  • regex-tool, muestra una ventana nueva en la que podemos trabajar para crear nuestras expresiones regulares cómodamente.
  • ¡Chequeos extra de seguridad! Es importante que los paquetes que nos descarguemos del gestor lo hagan desde fuentes oficiales y verificadas. Por lo tanto, verificaremos los certificados SSL para asegurarnos que conectamos con los verdaderos servidores y nadie los está suplantando.
  • visual-regexp, realiza un reemplazo de cadenas con expresiones regulares de forma visual.
  • annotate, nos permite hacer anotaciones en el código. No se guardarán en los mismos archivos, sino en un archivo de anotaciones aparte. Es muy útil para cuando dejamos para luegos, o queremos dejar una nota para nosotros sin que la vean otras personas.
  • Otros paquetes interesantes: restclient, async, s, restart-emacs
  • Algunas pequeñas configuraciones como: teclas para búsquedas en historial, abrir un terminal

Aquí tenemos el archivo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
;; Load misc modules and bind keys to them
(setq load-prefer-newer t)

;;(require-package 'neotree)

;;----------------------------------------------------------------------------
;; Uniquify - Provides unique names for files
;;----------------------------------------------------------------------------
(require 'uniquify)
(setq uniquify-buffer-name-style 'reverse)
(setq uniquify-separator " • ")
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re "^\\*")

;;----------------------------------------------------------------------------
;; Flycheck - real time code check
;;----------------------------------------------------------------------------
;(when (maybe-require-package 'flycheck)
;  (add-hook 'after-init-hook 'global-flycheck-mode)
;  (setq flycheck-display-errors-function #'flycheck-display-error-messages-unless-error-list))

;;----------------------------------------------------------------------------
;; Recent files
;;----------------------------------------------------------------------------

(recentf-mode 1)
(setq-default
 recentf-max-saved-items 1000
 recentf-exclude '("/tmp/"))

;;----------------------------------------------------------------------------
;; Smex - better minibuffer
;;----------------------------------------------------------------------------
(when (maybe-require-package 'smex)
  ;; Change path for ~/.smex-items
  (setq-default smex-save-file (expand-file-name ".smex-items" user-emacs-directory))
  (global-set-key [remap execute-extended-command] 'smex))

;; Stores command frequency. M-x keyfreq-show
(require-package 'keyfreq)
(keyfreq-mode 1)
(keyfreq-autosave-mode 1)

;;----------------------------------------------------------------------------
;; Spell
;;----------------------------------------------------------------------------
;(require-package 'ispell)
;(require-package 'flyspell)
;(require 'init-flyspell)

;;----------------------------------------------------------------------------
;; More things
;;----------------------------------------------------------------------------
(require-package 'htmlize)
(require-package 'regex-tool)
;(require-package 'yasnippet)
;(require 'yasnippet)
;(yas-global-mode 1)
(require-package 'visual-regexp)
(require 'visual-regexp)
(require-package 'annotate)
(require-package 'restclient)
(require-package 'async)                ;Async tasks
(require-package 's)                    ;String manipulation
(require-package 'restart-emacs)         ;Restart emacs from Emacs

;; When Emacs asks yes or no, make it possible to use just y or n
(fset 'yes-or-no-p 'y-or-n-p)
(add-hook 'annotate-mode-hook 'my/annotate-mode-stuff)

(defun my/annotate-mode-stuff ()
    (fci-mode 0)
    )

;; We could open annotate mode automatically, but I prefer doing it manually
;; (if (fboundp 'prog-mode)
;;     (add-hook 'prog-mode-hook 'annotate-mode)
;;   (dolist (hook '(lisp-mode-hook
;;                   emacs-lisp-mode-hook
;;                   scheme-mode-hook
;;                   clojure-mode-hook
;;                   ruby-mode-hook
;;                   yaml-mode
;;                   python-mode-hook
;;                   shell-mode-hook
;;                   php-mode-hook
;;                   css-mode-hook
;;                   haskell-mode-hook
;;                   caml-mode-hook
;;                   nxml-mode-hook
;;                   crontab-mode-hook
;;                   perl-mode-hook
;;                   tcl-mode-hook
;;                   javascript-mode-hook))
;;     (add-hook hook 'annotate-mode)))

;; Ido mode to navigate filesystem
(ido-mode t)
(setq ido-enable-flex-matching t
      ido-use-virtual-buffers t)

;; Open eshell in new window
(require-package 'eshell)
(setq eshell-where-to-jump 'begin)
(setq eshell-review-quick-commands nil)
(setq eshell-smart-space-goes-to-end t)
(defun eshell-other-window ()
  "Opens `eshell' in a new window."
  (interactive)
  (let ((buf (eshell)))
    (switch-to-buffer (other-buffer buf))
    (switch-to-buffer-other-window buf)))

;;----------------------------------------------------------------------------
;; Security. Check https://ogbe.net/emacsconfig.html
;;----------------------------------------------------------------------------
(setq tls-checktrust t)
(setq gnutls-verify-error t)
(let ((trustfile "/etc/ssl/cert.pem"))
  (setq tls-program
        `(,(format  "gnutls-cli --x509cafile %s -p %%p %%h" trustfile)
          ,(format "openssl s_client -connect %%h:%%p -CAfile %s -no_ssl2 -ign_eof" trustfile)))
  (setq gnutls-trustfiles (list trustfile)))


;;----------------------------------------------------------------------------
;; Configure keys
;;----------------------------------------------------------------------------
(global-set-key (kbd "s-r") 'recentf-open-files)
(global-set-key (kbd "C-x M-c") 'recentf-open-files)
(define-key global-map "\C-ce" 'eshell-other-window)

;; Define keys for minibuffer history search
(define-key minibuffer-local-map (kbd "<prior>") 'previous-complete-history-element)
(define-key minibuffer-local-map (kbd "<next>") 'next-complete-history-element)

(myemacs/elapsed-time)
(provide 'init-misc)

Inicialización de las ventanas

Emacs Windows in frame
Recordemos que en Emacs, el concepto de frame y window difiere de lo que estamos acostumbrados. Y es que Emacs, es más antiguo que nuestros conceptos de marco y ventana. Por lo tanto, para Emacs una ventana o window es cada una de las subdivisiones en las que podemos partir el punto de vista. Mientras que un frame es cada una de las nuevas ventanas que instanciamos de la aplicación. Al principio choca un poco, pero te terminas acostumbrando.
Por lo tanto, mi archivo init-windows.el será el siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
;; From https://github.com/purcell/emacs.d/blob/master/lisp/init-company.el

;;----------------------------------------------------------------------------
;; Navigate window layouts with "C-c <left>" and "C-c <right>"
;;----------------------------------------------------------------------------
(winner-mode 1)

;; Make "C-x o" prompt for a target window when there are more than 2
(require-package 'switch-window)
(require 'switch-window)
(setq-default switch-window-shortcut-style 'alphabet)
(setq-default switch-window-timeout nil)

;;----------------------------------------------------------------------------
;; When splitting window, show (other-buffer) in the new window
;;----------------------------------------------------------------------------
(defun split-window-func-with-other-buffer (split-function)
  (lexical-let ((s-f split-function))
    (lambda (&optional arg)
      "Split this window and switch to the new window unless ARG is provided."
      (interactive "P")
      (funcall s-f)
      (let ((target-window (next-window)))
        (set-window-buffer target-window (other-buffer))
        (unless arg
          (select-window target-window))))))


;; windmove keybindings on *nix systems with Alt key
(unless (memq window-system '(nt w32))
  (windmove-default-keybindings 'meta))

;;----------------------------------------------------------------------------
;; Key definitions
;;----------------------------------------------------------------------------
(global-set-key (kbd "C-x o") 'switch-window)
(global-set-key (kbd "C-x 2") (split-window-func-with-other-buffer 'split-window-vertically))
(global-set-key (kbd "C-x 3") (split-window-func-with-other-buffer 'split-window-horizontally))
(global-set-key (kbd "C-x 1") 'sanityinc/toggle-delete-other-windows)
(global-set-key (kbd "C-x |") 'split-window-horizontally-instead)
(global-set-key (kbd "C-x _") 'split-window-vertically-instead)
(global-set-key (kbd "<f7>") 'sanityinc/split-window)
(global-set-key (kbd "C-c <down>") 'sanityinc/toggle-current-window-dedication)


(myemacs/elapsed-time)
(provide 'init-windows)

Utilidades varias

En mi configuración, como dije al principio, he incluido utilidades de varios desarrolladores. Estas utilidades las estoy colocando dentro de ~/.emacs.d/lisp/lib/. Serán funciones que no trae Emacs de serie y que aumentan su funcionalidad. Todos estos ficheros se cargarán automáticamente cuando se ejecute init-utils.el:

misc.el

Encontramos utilidades para recargar, eliminar y renombrar buffers, entre otras. Poéis ejecutar algunas de estas funciones directamente con M-x o evaluando con M-:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
;; Those help functions found over the Internet...

;;----------------------------------------------------------------------------
;; Handier way to add modes to auto-mode-alist
;;----------------------------------------------------------------------------
(defun add-auto-mode (mode &rest patterns)
  "Add entries to `auto-mode-alist' to use `MODE' for all given file `PATTERNS'."
  (dolist (pattern patterns)
    (add-to-list 'auto-mode-alist (cons pattern mode))))

;;----------------------------------------------------------------------------
;; Delete the current file
;;----------------------------------------------------------------------------
(defun delete-this-file ()
  "Delete the current file, and kill the buffer."
  (interactive)
  (or (buffer-file-name) (error "No file is currently being edited"))
  (when (yes-or-no-p (format "Really delete '%s'?"
                             (file-name-nondirectory buffer-file-name)))
    (delete-file (buffer-file-name))
    (kill-this-buffer)))


;;----------------------------------------------------------------------------
;; Rename the current file
;;----------------------------------------------------------------------------
(defun rename-this-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive "sNew name: ")
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (unless filename
      (error "Buffer '%s' is not visiting a file!" name))
    (progn
      (when (file-exists-p filename)
        (rename-file filename new-name 1))
      (set-visited-file-name new-name)
      (rename-buffer new-name))))

;;----------------------------------------------------------------------------
;; Browse current HTML file
;;----------------------------------------------------------------------------
(defun browse-current-file ()
  "Open the current file as a URL using `browse-url'."
  (interactive)
  (let ((file-name (buffer-file-name)))
    (if (and (fboundp 'tramp-tramp-file-p)
             (tramp-tramp-file-p file-name))
        (error "Cannot open tramp file")
      (browse-url (concat "file://" file-name)))))

;;----------------------------------------------------------------------------
;; Reverts current buffer without asking for confirmation
;;----------------------------------------------------------------------------
(defun revert-buffer-no-confirm ()
  "Revert buffer without confirmation."
  (interactive) (revert-buffer t t))

;;----------------------------------------------------------------------------
;; Kills all other buffers
;;----------------------------------------------------------------------------
(defun kill-other-buffers ()
      "Kill all other buffers."
      (interactive)
      (mapc 'kill-buffer (delq (current-buffer) (buffer-list))))

sachachua.el

Algunas funciones tomadas prestado de Sacha Chua:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
;; Increase-decrease functions from Sacha Chua
(defun sacha/increase-font-size ()
  (interactive)
  (set-face-attribute 'default
                      nil
                      :height
                      (ceiling (* 1.10
                                  (face-attribute 'default :height)))))
(defun sacha/decrease-font-size ()
  (interactive)
  (set-face-attribute 'default
                      nil
                      :height
                      (floor (* 0.9
                                  (face-attribute 'default :height)))))

;; Not original from Sacha. Taken from: http://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/
(defun sacha/smarter-move-beginning-of-line (arg)
  "Move point back to indentation of beginning of line.

Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.

If ARG is not nil or 1, move forward ARG - 1 lines first.  If
point reaches the beginning or end of the buffer, stop there."

  (interactive "^p")
  (setq arg (or arg 1))

  ;; Move lines first
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))

  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))

prelude.el

Funciones tomadas de la distribución Prelude. Por ahora sólo una, pero abierto está para introducir más… Esta función, copia al portapapeles el nombre de fichero actual, es algo muy útil de vez en cuando.

1
2
3
4
5
6
7
8
9
10
;; Functions by bbatsov's Prelude Emacs Distribution
(defun prelude/copy-file-name-to-clipboard ()
  "Copy the current buffer file name to the clipboard."
  (interactive)
  (let ((filename (if (equal major-mode 'dired-mode)
                      default-directory
                    (buffer-file-name))))
    (when filename
      (kill-new filename)
      (message "Copied buffer file name '%s' to the clipboard." filename))))

sanityinc.el

Son funciones y utilidades creadas por Steve Purcell. Hay de todo, están más o menos documentadas y muy probadas. Tal vez algunas como las que manejan transparencia no funcionen en todos los entornos, aunque deberían. Pero sólo tenemos que echarles un ojo.
También cuenta con algunos trucos para que popup.el no haga cosas raras en la pantalla en combinación con el famoso fci-mode del que hablaremos en un futuro post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
;;----------------------------------------------------------------------------
;; Some cool functions
;;----------------------------------------------------------------------------
;; These functions borrowed from Steve Purcell's work: https://github.com/purcell

;; MISC FUNCTIONS

;;----------------------------------------------------------------------------
;; Adjust window opacity
;;----------------------------------------------------------------------------
(defun sanityinc/adjust-opacity (frame incr)
  "Adjust the background opacity of FRAME by increment INCR."
  (unless (display-graphic-p frame)
    (error "Cannot adjust opacity of this frame"))
  (let* ((oldalpha (or (frame-parameter frame 'alpha) 100))
         ;; The 'alpha frame param became a pair at some point in
         ;; emacs 24.x, e.g. (100 100)
         (oldalpha (if (listp oldalpha) (car oldalpha) oldalpha))
         (newalpha (+ incr oldalpha)))
    (when (and (<= frame-alpha-lower-limit newalpha) (>= 100 newalpha))
      (modify-frame-parameters frame (list (cons 'alpha newalpha))))))

;;----------------------------------------------------------------------------
;; String utilities missing from core emacs
;;----------------------------------------------------------------------------
(defun sanityinc/string-all-matches (regex str &optional group)
  "Find all matches for `REGEX' within `STR', returning the full match string or group `GROUP'."
  (let ((result nil)
        (pos 0)
        (group (or group 0)))
    (while (string-match regex str pos)
      (push (match-string group str) result)
      (setq pos (match-end group)))
    result))

(defun sanityinc/string-rtrim (str)
  "Remove trailing whitespace from `STR'."
  (replace-regexp-in-string "[ \t\n]+$" "" str))


;;----------------------------------------------------------------------------
;; Find the directory containing a given library
;;----------------------------------------------------------------------------
(autoload 'find-library-name "find-func")
(defun sanityinc/directory-of-library (library-name)
  "Return the directory in which the `LIBRARY-NAME' load file is found."
  (file-name-as-directory (file-name-directory (find-library-name library-name))))

;; PACKAGE FUNCTIONS

;;----------------------------------------------------------------------------
;; If gpg cannot be found, signature checking will fail, so we
;; conditionally enable it according to whether gpg is available. We
;; re-run this check once $PATH has been configured
;;----------------------------------------------------------------------------
(defun sanityinc/package-maybe-enable-signatures ()
  (setq package-check-signature (when (executable-find "gpg") 'allow-unsigned)))

(defun sanityinc/set-tabulated-list-column-width (col-name width)
  "Set any column with name COL-NAME to the given WIDTH."
  (when (> width (length col-name))
    (cl-loop for column across tabulated-list-format
             when (string= col-name (car column))
             do (setf (elt column 1) width))))

(defun sanityinc/maybe-widen-package-menu-columns ()
  "Widen some columns of the package menu table to avoid truncation."
  (when (boundp 'tabulated-list-format)
    (sanityinc/set-tabulated-list-column-width "Version" 13)
    (let ((longest-archive-name (apply 'max (mapcar 'length (mapcar 'car package-archives)))))
      (sanityinc/set-tabulated-list-column-width "Archive" longest-archive-name))))

;; Compilations

(defun sanityinc/alert-after-compilation-finish (buf result)
  "Use `alert' to report compilation RESULT if BUF is hidden."
  (unless (catch 'is-visible
            (walk-windows (lambda (w)
                            (when (eq (window-buffer w) buf)
                              (throw 'is-visible t))))
            nil)
    (alert (concat "Compilation " result)
           :buffer buf
           :category 'compilation)))

;;----------------------------------------------------------------------------
;; Delete other windows but current
;;----------------------------------------------------------------------------
(defun sanityinc/toggle-delete-other-windows ()
  "Delete other windows in frame if any, or restore previous window config."
  (interactive)
  (if (and winner-mode
           (equal (selected-window) (next-window)))
      (winner-undo)
    (delete-other-windows)))

;;----------------------------------------------------------------------------
;; Rearrange split windows
;;----------------------------------------------------------------------------
(defun split-window-horizontally-instead ()
  (interactive)
  (save-excursion
    (delete-other-windows)
    (funcall (split-window-func-with-other-buffer 'split-window-horizontally))))

(defun split-window-vertically-instead ()
  (interactive)
  (save-excursion
    (delete-other-windows)
    (funcall (split-window-func-with-other-buffer 'split-window-vertically))))


;; Borrowed from http://postmomentum.ch/blog/201304/blog-on-emacs
(defun sanityinc/split-window()
  "Split the window to see the most recent buffer in the other window.
Call a second time to restore the original window configuration."

  (interactive)
  (if (eq last-command 'sanityinc/split-window)
      (progn
        (jump-to-register :sanityinc/split-window)
        (setq this-command 'sanityinc/unsplit-window))
    (window-configuration-to-register :sanityinc/split-window)
    (switch-to-buffer-other-window nil)))


;;----------------------------------------------------------------------------
;; Window dedication
;;----------------------------------------------------------------------------
(defun sanityinc/toggle-current-window-dedication ()
  "Toggle whether the current window is dedicated to its current buffer."
  (interactive)
  (let* ((window (selected-window))
         (was-dedicated (window-dedicated-p window)))
    (set-window-dedicated-p window (not was-dedicated))
    (message "Window %sdedicated to %s"
             (if was-dedicated "no longer " "")
             (buffer-name))))


(defvar sanityinc/last-compilation-buffer nil
  "The last buffer in which compilation took place.")

;; Workaround to avoid weird behaviour with fci-mode and popup
(defvar sanityinc/fci-mode-suppressed nil)
(defadvice popup-create (before suppress-fci-mode activate)
  "Suspend fci-mode while popups are visible"
  (set (make-local-variable 'sanityinc/fci-mode-suppressed) fci-mode)
  (when fci-mode
    (turn-off-fci-mode)))
(defadvice popup-delete (after restore-fci-mode activate)
  "Restore fci-mode when all popups have closed"
  (when (and (not popup-instances) sanityinc/fci-mode-suppressed)
    (setq sanityinc/fci-mode-suppressed nil)
    (turn-on-fci-mode)))

myemacs.el

Son funciones que he recopilado, modificado o hecho yo. Por ahora voy a enseñar sólo dos, para poner el modo pantalla completa y para monitorizar el tiempo de carga de Emacs. Aunque en futuras versiones habrá algunas funciones más.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
;;----------------------------------------------------------------------------
;; Some cool functions
;;----------------------------------------------------------------------------
;; These functions are made by me (Twitter: @gaspar_fm; GitHub: gasparfm) or
;; heavily modified by me

;;----------------------------------------------------------------------------
;; Toggles fullscreen
;;----------------------------------------------------------------------------
(defun myemacs/toggle-fullscreen ()
       (interactive)
       (x-send-client-message nil 0 nil "_NET_WM_STATE" 32
                 '(2 "_NET_WM_STATE_FULLSCREEN" 0)))

(defun myemacs/elapsed-time ()
  (let ((elapsed (float-time (time-subtract (current-time)
                                            emacs-start-time))))
    (message "[STARTUP] Loading %s ... done (%.3fs)" load-file-name elapsed)))

Mis teclas personalizadas

En estos ficheros he definido muchas teclas y acciones personalizadas:

  • Click con el botón izquierdo en un número de línea: selecciona dicha línea
  • Arrastral ratón desde un número de línea a otro: selecciona todas esas líneas
  • C-z : NO minimiza la ventana
  • C-c e: Abre un terminal eshell
  • C-x o: Cambia ventana, comportamiento habitual, aunque cuando hay más de 2 ventanas mostrará una pantalla especial para seleccionar la ventana a la que vamos a saltar.
  • C-x 2: Divide las ventanas de manera vertical (comportamiento habitual, modificado ligeramente)
  • C-x 3: Divide las ventanas de manera horizontal (comportamiento habitual, modificado ligeramente)
  • C-x 1: Elimina todas las ventanas menos la actual.
  • C-x |: Crea dos ventanas verticales.
  • C-x _: Crea dos ventanas horizontales.
  • C-x 2: Divide las ventanas de manera vertical (comportamiento habitual, modificado ligeramente)
  • C-c down (Control+abajo): Hace que la ventana actual sólo pueda mostrar el buffer actual (o lo deshace).
  • M-down (Alt+Abajo) : Disminuye la visibilidad de la ventana (aumenta transparencia)
  • M-up (Alt+Arriba): Aumenta la visibilidad de la ventana.
  • M-0 (Alt+0): Ventana visible al 100%, reset de opacidad.
  • M-f (Alt+f): Modo pantalla completa (fullscreen).
  • s-C-+ (Super+Control+ +): Aumenta el tamaño de letra.
  • s-C– (Super+Control+ -): Disminuye el tamaño de letra.
  • F12: Recarga el fichero actual descartando cambios y sin preguntar.
  • s-h (Super+h): Resalta la línea actual en todos los buffers.
  • s-r (Super+r): Abrir archivos recientes.
  • M-% (Alt+%): Reemplazar texto
  • s- (Super-Espacio): Reemplazar texto (tecla alternativa, aunque puede que choque con algunos entornos de ventanas
  • F7: Muestra el buffer más reciente en la “otra” ventana. Si se pulsa de nuevo, vuelve a mostrar el buffer que había anteriormente.

En el minibuffer

  • <prior> (Página atrás) : Autocompletado con valores previos del historial. (Comportamiento como readline aquí)
  • <next> (Página adelante) : Autocompletado con valores siguientes del historial. (Comportamiento como readline aquí)

Siguiente post

En el siguiente post tocaré temas como sesiones y ayuda para la edición de texto. Se utilizarán algunas funciones dentro de lib. Y empezará a tomar algo de forma el entorno con muchas utilidades.

También podría interesarte....

There are 6 comments left Ir a comentario

  1. Pingback: Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (I – Configuración general) | PlanetaLibre /

  2. nadie /
    Usando Mozilla Firefox Mozilla Firefox 57.0 en Fedora Linux Fedora Linux

    gracias, para los voyeuristas del init.el un post asi siempre es impagable… o, por cierto el post sobre el operador ternario anterior, aplicado a funciones en C me dejo todo loco$

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 57.0 en Ubuntu Linux Ubuntu Linux

      ¡¡Gracias a ti!! Espero que para hoy o mañana salga el siguiente de mi configuración de Emacs, con mucho código y muchas cosas interesantes.

  3. Pingback: Paquetes Emacs: try, wich-keys y org-bullets. | Quijote Libre /

  4. Pingback: Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (II – Edición de código) – Poesía Binaria /

  5. Pingback: Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (III – Lenguajes de programación) – Poesía Binaria /

Leave a Reply