Suelo utilizar Emacs para editar código fuente para todos mis programas, tanto proyectos grandes como pequeños. Y, para crear muchos de los ejemplos que pongo en el blog (Aunque a veces la indentación no se copia bien). Por eso, es muy importante, tener herramientas para poder utilizar sin problemas y cómodamente el código en nuestro editor.
En este post, como ya empecé hace unos días (ver aquí Mi configuración personalizada para Emacs, primera parte) seguiré comentando punto por punto mi configuración actual de Emacs, con las utilidades a mi gusto. Aunque la configuración actualizada la tengo en GitHub, vamos a ver con detalle las partes dedicadas a ayuda de edición de código, orientado sobre todo a hacer más llevadera la programación con utilidades que mejoran nuestra experiencia de usuario y sesiones.
init.el
La configuración que muestro aquí está dividida en varios Archivos está dividida en múltiples archivos divididos por categorías. En el post anterior vemos más detalles sobre el árbol de directorios y el contenido de init.el así como la forma en la que tenemos que incluir los archivos que presento aquí.
De todas formas, este código puede ser evaluado con M-x eval-expression / M-x eval-region (dependiendo de dónde lo escribamos). Incluso es recomendable abrir un buffer nuevo (o *scratch*) para practicar y probar cosas.
Gestión de sesiones – init-sessions.el
En este archivo:
- Establecemos configuración de las sesiones y bloqueos. En mi caso, dependiendo del directorio desde el que se abra Emacs. Así como el auto-salvado de la sesión, a los 10 minutos (600 segundos)
- Sobreescribo funciones de lectura del desktop para mostrar el tiempo invertido en restaurar el desktop.
- Hacemos que se restauren más aspectos de la sesión de Emacs con el session-mode.
- Se establecen valores por defecto del tamaño de los historiales que guardará Emacs. No los pongo ilimitados para que se optimice un poco el uso de la memoria y las búsquedas sean más rápidas.
- Configuramos las copias de seguridad. Éstas utilizarán el modo backup-by-copying, aunque es el más lento, de otro modo, la copia de seguridad se hace renombrando y en algunos sistemas de archivos puede dar problemas. Por otro lado, se mantendrán varias versiones de los archivos y todas las copias de seguridad se harán en el directorio ./.__backups__ dentro del directorio actual. Muchos prefieren guardar las copias de seguridad en otro sitio, podemos darle una ruta absoluta si queremos guardar todas las copias de seguridad juntas.
- También me gusta la forma en la que Emacs ofrece salvado automático de archivos aunque podremos establecer el directorio que deseemos.
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 | ;; From https://github.com/purcell/emacs.d/blob/master/lisp/init-sessions.el ;; Using some settings from https://github.com/alexott/emacs-configs/blob/master/rc/emacs-rc-desktop.el ;; save a list of open files in ~/.emacs.d/.emacs.desktop (setq-default desktop-missing-file-warning nil) (setq emacs-sessions-directory (expand-file-name "sessions/" user-emacs-directory)) (setq emacs-desktop-file (concat "emacs_" (secure-hash 'md5 default-directory) ".desktop")) (setq emacs-desktop-lock (concat "lock_" (secure-hash 'md5 default-directory))) (setq desktop-path (list emacs-sessions-directory) desktop-base-file-name emacs-desktop-file desktop-base-lock-name emacs-desktop-lock desktop-save t desktop-files-not-to-save "^$" ;reload tramp paths desktop-auto-save-timeout 600) (desktop-save-mode 1) (defadvice desktop-read (around time-restore activate) (let ((start-time (current-time))) (prog1 ad-do-it (message "Desktop restored in %.2fms" (float-time (time-subtract (current-time) emacs-start-time)))))) (defadvice desktop-create-buffer (around time-create activate) (let ((start-time (current-time)) (filename (ad-get-arg 1))) (prog1 ad-do-it (message "Desktop: %.2fms to restore %s" (float-time (time-subtract (current-time) emacs-start-time)) (when filename (abbreviate-file-name filename)))))) ;;---------------------------------------------------------------------------- ;; Restore histories and registers after saving ;;---------------------------------------------------------------------------- (setq-default history-length 1000) (savehist-mode t) (require-package 'session) (setq session-save-file (expand-file-name ".session" user-emacs-directory)) (setq session-name-disable-regexp "\\(?:\\`'/tmp\\|\\.git/[A-Z_]+\'\\)") (add-hook 'after-init-hook 'session-initialize) ;;---------------------------------------------------------------------------- ;; Histories configuration ;;---------------------------------------------------------------------------- ;; save a bunch of variables to the desktop file ;; for lists specify the len of the maximal saved data also (setq desktop-globals-to-save (append '((comint-input-ring . 50) (compile-history . 30) desktop-missing-file-warning (dired-regexp-history . 20) (extended-command-history . 30) (face-name-history . 20) (file-name-history . 100) (grep-find-history . 30) (grep-history . 30) (ido-buffer-history . 100) (ido-last-directory-list . 100) (ido-work-directory-list . 100) (ido-work-file-list . 100) (ivy-history . 100) (magit-read-rev-history . 50) (minibuffer-history . 50) (org-clock-history . 50) (org-refile-history . 50) (org-tags-history . 50) (query-replace-history . 60) (read-expression-history . 60) (regexp-history . 60) (regexp-search-ring . 20) register-alist (search-ring . 20) (shell-command-history . 50) tags-file-name tags-table-list))) (when (eval-when-compile (and (>= emacs-major-version 24) (version emacs-version "24.3.50") )) (unless (boundp 'desktop-restore-frames) (require-package 'frame-restore) (frame-restore))) ;;---------------------------------------------------------------------------- ;; Backups and auto-save ;;---------------------------------------------------------------------------- ;; Backups management (file~) (setq backup-by-copying t) (setq backup-directory-alist '(("." . ".__backups__"))) (setq delete-old-versions t kept-new-versions 6 kept-old-versions 2 version-control t) ;; Autosave backups management (#file#) Emacs Wiki, https://www.emacswiki.org/emacs/AutoSave (defconst emacs-tmp-dir (expand-file-name (format "emacs%d" (user-uid)) temporary-file-directory)) (setq auto-save-file-name-transforms `((".*" ,emacs-tmp-dir t))) (setq auto-save-list-file-prefix emacs-tmp-dir) (myemacs/elapsed-time) (provide 'init-sessions) |
init-cedet.el
CEDET (Collection of Emacs Development Environment Tools o Colección de Herramientas de Entorno de Desarrollo de Emacs) es una de las joyas de Emacs que permiten aumentar la funcionalidad de este editor a otro nivel. En CEDET tenemos muchas utilidades que harán que programemos mucho más rápido. Algunos de los módulos que encontramos en CEDET son:
- Semantic: Analiza nuestro código para ofrecernos autocompletado inteligente, resaltar declaraciones de funciones, buscar implementaciones, navegar dentro de nuestro código, buscar errores, etc.
- EDE: Emacs Development Environment (Entorno de Desarrollo de Emacs), nos proporciona un entorno de gestión de proyectos.
- SRE: Gestor de plantillas
- ECB: Emacs Code Browser (Navegador de Código de Emacs). Nos presentará una configuración de ventanas en la que incluirá un navegador de archivos y directorios, un navegador de objetos dentro de nuestro código y un historial además de la ventana de nuestro código. Es más o menos lo que se ve en la imagen de este apartado.
- Otros: Podemos instalar una barra de acceso rápido a elementos (Speedbar), tenemos un módulo para crear diagramas (COGRE), y muchas funciones Lisp que pueden utilizar otros módulos para Emacs.
Nuestro archivo init-cedet.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 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 | ;; Based on Alex Ott's minimal-cedet-config : https://gist.github.com/alexott/3930120 ;; and others ;; Use builtin CEDET instead of installing new CEDET version (require 'cedet) ;; Load contrib directory (add-to-list 'load-path (file-name-as-directory (expand-file-name "site-lisp/cedet-contrib/" user-emacs-directory)) "contrib") ;; Workarounds for Emacs (when (eval-when-compile (version "25" emacs-version)) (setq fixes-root-path (file-name-as-directory (expand-file-name "lisp/fixes/" user-emacs-directory))) (load-file (concat fixes-root-path "hideif.el"))) ;; SemanticDB installed by default (add-to-list 'semantic-default-submodes 'global-semanticdb-minor-mode t) ;; Buffer parsing when idle (add-to-list 'semantic-default-submodes 'global-semantic-idle-scheduler-mode 1) ;; Display current function on header (add-to-list 'semantic-default-submodes 'global-semantic-stickyfunc-mode 1) ;; Highlight the declaration of current function (add-to-list 'semantic-default-submodes 'global-semantic-highlight-func-mode 1) ;; Display function info in minibuffer (add-to-list 'semantic-default-submodes 'global-semantic-idle-summary-mode t) ;; srecode!! (add-to-list 'semantic-default-submodes 'global-srecode-minor-mode t) ;; Show parser status when parsing lasts long (add-to-list 'semantic-default-submodes 'global-show-parser-state-mode t) ;; Decorates some parts of the file due to the position where we are (add-to-list 'semantic-default-submodes 'global-semantic-decoration-mode t) ;; Underline in red everything it cannot parse (add-to-list 'semantic-default-submodes 'global-semantic-show-unmatched-syntax-mode) ;; Most Recently Used tags/functions fast jump (add-to-list 'semantic-default-submodes 'global-semantic-mru-bookmark-mode) (setq-default semantic-idle-scheduler-idle-time 5) ;I'm idle when I've been 5 seconds out (setq-default semantic-idle-scheduler-verbose-flag 't) ;Tell me when you are working (semantic-mode) ;; These may be automatically required, but I had them from previous versions (require 'semantic/bovine/c) (require 'semantic/bovine/gcc) (require 'semantic/bovine/grammar) (require 'semantic/bovine/make) (require 'semantic/bovine/scm) (require 'semantic/idle) (require 'semantic/db) (require 'semantic/ia) (require 'semantic/analyze) (require 'eassist) ;; customisation of modes (defun gasparfm/cedet-hook () (local-set-key [(control return)] 'semantic-ia-complete-symbol-menu) (local-set-key "\C-c?" 'semantic-ia-complete-symbol) ;; (local-set-key "\C-c>" 'semantic-complete-analyze-inline) (local-set-key "\C-c=" 'semantic-decoration-include-visit) ;; C-c+j = Fast jump (local-set-key "\C-cj" 'semantic-ia-fast-jump) ;; C-c+q = Show doc (local-set-key "\C-cq" 'semantic-ia-show-doc) ;; C-c+s Show Summary (local-set-key "\C-cs" 'semantic-ia-show-summary) ;; C-c+p = Toggle between prototype and function (local-set-key "\C-cp" 'semantic-analyze-proto-impl-toggle) ;; C-c+m = Switch MRU tags (local-set-key "\C-cp" 'semantic-mrub-switch-tags) ) (add-hook 'c-mode-common-hook 'gasparfm/cedet-hook) (add-hook 'lisp-mode-hook 'gasparfm/cedet-hook) (add-hook 'scheme-mode-hook 'gasparfm/cedet-hook) (add-hook 'emacs-lisp-mode-hook 'gasparfm/cedet-hook) (add-hook 'erlang-mode-hook 'gasparfm/cedet-hook) (add-to-list 'semantic-dependency-system-include-path '/usr/include/c++/5/bits) (semanticdb-enable-gnu-global-databases 'c-mode t) (semanticdb-enable-gnu-global-databases 'c++-mode t) ;; SRecode ;; Delete ~/.emacs.d/srecode-map.el if any problems loading (require 'srecode) (global-srecode-minor-mode 1) ;; EDE (require 'ede) (global-ede-mode 1) (ede-enable-generic-projects) (require-package 'ecb) (setq-default ecb-tip-of-the-day nil) (myemacs/elapsed-time) (provide 'init-cedet) |
Lo primero que hacemos es cargar el módulo cedet que viene con Emacs. Aunque podemos descargarlo de Internet, con muchos módulos y más completo, el que viene con Emacs suele ser más nuevo y compatible con nuestra versión de Emacs. Puede que si lo descargamos, aunque sea desde la página oficial, tengamos ciertos problemas para echarlo a andar.
Luego cargamos en el load-path de Emacs el contenido de site-lisp/cedet-contrib. Dentro de este directorio (que podemos ver en el GitHub de mi configuración) he incluido algunos archivos que vienen con la instalación original de CEDET y no vienen en el core, pero que son interesantes.
Además, incluyo un archivo hideif.el que suele venir con Emacs y, tiene un bug en versiones inferiores a Emacs 25 (creo que en la 24.5 ya lo arreglaron, pero no llegué a probarlo). CEDET utiliza este módulo internamente y puede provocar algunos avisos, incluso hacer que no inicie Emacs correctamente.
Una vez cargado el módulo, vemos qué partes vamos a configurar. En mi caso, me quedo con Semantic, EDE, SREcode, ECB y EAssist (parte de los módulos de contrib que usan CEDET por debajo).
Semantic, que parsea el contenido de mis archivos de código leyendo variables, funciones, clases, espacios de nombres y demás lo suelo usar para asistirme mientras codifico. Esta asistencia suele ser el autocompletado inteligente, que me da opciones relativas a lo que estoy escribiendo. Puede darme opciones tanto del propio lenguaje como de bibliotecas que esté incluyendo y sean accesibles. Está pensado para C y C++, aunque para otros lenguajes también funciona bien y me permite trabajar rápidamente. Aunque está hecho en un lenguaje interpretado funciona muy rápido, e incluso hay formas de hacer que funcione más rápido aún, ayudándonos de aplicaciones externas, yo prefiero que mi configuración de Emacs sea lo más portable posible, y llevándome el directorio .emacs.d o incluso clonándolo de GitHub sea suficiente. También se usa para que ECB muestre información sobre los elementos que se encuentran en el código y pueda ir directamente a funciones, variables o estructuras determinadas o, por ejemplo, para que cuando programo en C o C++ pueda saltar directamente de un archivo de cabecera a uno de código sin tener que hacerlo a mano, dirigiéndome directamente al número de línea concreto donde está una función, una llamada, etc.
Aunque Semantic tiene muchas posibles configuraciones, en mi configuración personal incluiré:
- semanticdb-minor-mode. Como parsear archivos de código fuente es una tarea pesada que puede dejar a Emacs colgado durante varios segundos o minutos. Haremos que Semantic sólo lea los archivos cuando haya cosas nuevas, almacenando en caché los elementos que ya ha leído y optimizando así su funcionamiento.
- global-semantic-idle-scheduler-mode. Analiza archivos cuando no estoy directamente trabajando con Emacs. Porque es muy incómodo que, mientras estás trabajando, el editor se pare y no te deje hacer nada, aunque sea durante unos segundos. Por eso, aunque a veces, cuando utilizamos el autocompletado puede forzarse un parseo de los archivos, es interesante que, mientras no estemos trabajando se vayan completando ciertas partes del mismo. Personalmente, lo suelo configurar con semantic-idle-scheduler-idle-time indicando que se considere que no hago nada cuando lleve 5 segundos sin utilizar Emacs, y semantic-idle-scheduler-verbose-flag para que me indique en pantalla cuándo está trabajando Semantic.
- global-semantic-stickyfunc-mode coloca en la parte superior del buffer el prototipo de la función dentro de la que estamos. Es muy útil cuando las funciones son muy grandes, o nuestra pantalla muy pequeña, para tener siempre presente el nombre de la función y los argumentos de entrada.
- global-semantic-highlight-func-mode. Resalta llamadas o declaraciones de funciones mientras tenemos el cursor encima. Así sabemos dónde se llama, y dónde se declara una función.
- global-semantic-idle-summary-mode. Nos muestra la cabecera de las funciones en el minibuffer ayudándonos a ver los argumentos de una función, método o llamada de un vistazo.
- srecode-minor-mode. Activa el sistema de plantillas SREcode.
- global-show-parser-state-mode. Cuando esté realizando un parseo de archivos fuente, decirnos el progreso del parseo en el minibuffer. Siempre es algo más lento decir cuánto lleva completado de una tarea e ir actualizando ese progreso, pero desespera menos al que pacientemente está mirando pasmado la pantalla esperando que se realice la tarea.
- global-semantic-decoration-mode. Aplica una decoración a cada palabra clave del código.
- global-semantic-show-unmatched-syntax-mode. Nos avisa cuando hay errores de sintaxis según el parseador. Puede que algo no sea un error de sintaxis propiamente dicho, en códigos muy complejos, tal vez el parser no llegue a lo que nosotros queremos, pero generalmente funciona bien.
- global-semantic-mru-bookmark-mode. Nos permite saltar entre etiquetas (variables, funciones, etc) para dirigirnos a la parte del código donde se declaran/definen/llaman con M-n, M-p
- También establecemos algunas teclas rápidas para llamar algunos métodos de semantic. Podemos verlo al final del post, en teclas rápidas y se añaden a un hook de determinados lenguajes.
Tras esto, se añaden algunas características para mejorar el parseo de las bibliotecas de C y C++.
Utilidades de edición (init-editing-utils.el)
Ahora vamos a ver algunas utilidades que nos ayudarán a utilizar nuestro entorno de programación, o configurarlo de manera fina, a nuestro gusto. Nota: El gusto de cada uno puede variar, aunque aquí tenéis un punto de partida.
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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | ;; From : https://github.com/purcell/emacs.d/blob/master/lisp/init-editing-utils.el ;; and others (require-package 'diminish) (require-package 'unfill) (require-package 'focus) ;; Automatic pairs open symbols (, {, [... ;; (when (fboundp 'electric-pair-mode) ;; (electric-pair-mode)) (when (eval-when-compile (version "24.4" emacs-version)) (electric-indent-mode 1)) ;;---------------------------------------------------------------------------- ;; Some basic preferences ;;---------------------------------------------------------------------------- (setq-default blink-cursor-interval 0.4 bookmark-default-file (expand-file-name ".bookmarks.el" user-emacs-directory) buffers-menu-max-size 30 case-fold-search t column-number-mode t delete-selection-mode t ediff-split-window-function 'split-window-horizontally ediff-window-setup-function 'ediff-setup-windows-plain indent-tabs-mode t make-backup-files t auto-save-interval 180 ;Auto save every 180 secs mouse-yank-at-point t save-interprogram-paste-before-kill t scroll-preserve-screen-position 'always set-mark-command-repeat-pop t tooltip-delay 0.5 truncate-lines nil truncate-partial-width-windows nil) (global-auto-revert-mode) (setq global-auto-revert-non-file-buffers t auto-revert-verbose nil) (transient-mark-mode t) ;;; Newline behaviour (defun sanityinc/newline-at-end-of-line () "Move to end of line, enter a newline, and reindent." (interactive) (move-end-of-line 1) (newline-and-indent)) (when (eval-when-compile (string "24.3.1" emacs-version)) ;; https://github.com/purcell/emacs.d/issues/138 (after-load 'subword (diminish 'subword-mode))) ;; Some bugs with indent-guide-mode and autocomplete-mode ;; (when (maybe-require-package 'indent-guide) ;; (add-hook 'prog-mode-hook 'indent-guide-mode) ;; (after-load 'indent-guide ;; (diminish 'indent-guide-mode))) ;;(require-package 'nlinum) (when (require-package 'rainbow-delimiters) (add-hook 'prog-mode-hook 'rainbow-delimiters-mode)) (when (fboundp 'global-prettify-symbols-mode) (global-prettify-symbols-mode)) (require-package 'undo-tree) (global-undo-tree-mode) (diminish 'undo-tree-mode) (require-package 'highlight-symbol) (dolist (hook '(prog-mode-hook html-mode-hook css-mode-hook)) (add-hook hook 'highlight-symbol-mode) (add-hook hook 'highlight-symbol-nav-mode)) (add-hook 'org-mode-hook 'highlight-symbol-nav-mode) (after-load 'highlight-symbol (diminish 'highlight-symbol-mode) (defadvice highlight-symbol-temp-highlight (around sanityinc/maybe-suppress activate) "Suppress symbol highlighting while isearching." (unless (or isearch-mode (and (boundp 'multiple-cursors-mode) multiple-cursors-mode)) ad-do-it))) ;;---------------------------------------------------------------------------- ;; Zap *up* to char is a handy pair for zap-to-char ;;---------------------------------------------------------------------------- (autoload 'zap-up-to-char "misc" "Kill up to, but not including ARGth occurrence of CHAR.") (global-set-key (kbd "M-Z") 'zap-up-to-char) ;;---------------------------------------------------------------------------- ;; Browse kill ring ;;---------------------------------------------------------------------------- (require-package 'browse-kill-ring) (setq browse-kill-ring-highlight-inserted-item 'pulse) (setq browse-kill-ring-highlight-current-entry t) (setq browse-kill-ring-show-preview t) (setq browse-kill-ring-separator "\f") (global-set-key (kbd "s-Y") 'browse-kill-ring) (after-load 'browse-kill-ring (define-key browse-kill-ring-mode-map (kbd "C-g") 'browse-kill-ring-quit) (define-key browse-kill-ring-mode-map (kbd "M-n") 'browse-kill-ring-forward) (define-key browse-kill-ring-mode-map (kbd "M-p") 'browse-kill-ring-previous) (define-key browse-kill-ring-mode-map (kbd " (define-key browse-kill-ring-mode-map (kbd " (define-key browse-kill-ring-mode-map (kbd " |
This post first appeared on PoesÃa Binaria - Programación, TecnologÃa Y Sof, please read the originial post: here