Poesía Binaria

Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (II – Edición de código)


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:

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:

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 < 25
(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é:

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 "<down>") 'browse-kill-ring-forward)
  (define-key browse-kill-ring-mode-map (kbd "<up>") 'browse-kill-ring-previous)
  (define-key browse-kill-ring-mode-map (kbd "<backtab>") 'browse-kill-ring-previous) ;Shift + TAB
  )
(after-load 'page-break-lines
  (push 'browse-kill-ring-mode page-break-lines-modes))

;; Forward yank
(defun yank-pop-forwards (arg)
      (interactive "p")
      (yank-pop (- arg)))
    (global-set-key "\M-Y" 'yank-pop-forwards) ; M-Y (Meta-Shift-Y)

;;----------------------------------------------------------------------------
;; Don't disable narrowing commands
;;----------------------------------------------------------------------------
(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'narrow-to-defun 'disabled nil)

;;----------------------------------------------------------------------------
;; Show matching parens
;;----------------------------------------------------------------------------
(show-paren-mode 1)

;;----------------------------------------------------------------------------
;; Expand region
;;----------------------------------------------------------------------------
(require-package 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)

;;----------------------------------------------------------------------------
;; Don't disable case-change functions
;;----------------------------------------------------------------------------
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)

;;----------------------------------------------------------------------------
;; Don't disable these functions
;;----------------------------------------------------------------------------
(put 'scroll-left 'disabled nil)
(put 'eval-expression 'disabled nil)

;;----------------------------------------------------------------------------
;; Rectangle selections, and overwrite text when the selection is active
;;----------------------------------------------------------------------------
(cua-selection-mode t)                  ; for rectangles, CUA is nice


;; Dont delete selected text when pressing key. C-w
(setq delete-selection-mode nil)

;;----------------------------------------------------------------------------
;; Handy key bindings
;;----------------------------------------------------------------------------
(global-set-key (kbd "C-.") 'set-mark-command)
(global-set-key (kbd "C-x C-.") 'pop-global-mark)

;; (when (maybe-require-package 'avy)
;;   (global-set-key (kbd "C-;") 'avy-goto-word-or-subword-1))

(require-package 'multiple-cursors)
;; multiple-cursors
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-+") 'mc/mark-next-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
;; From active region to multiple cursors:
(global-set-key (kbd "C-c c r") 'set-rectangular-region-anchor)
(global-set-key (kbd "C-c c c") 'mc/edit-lines)
(global-set-key (kbd "C-c c e") 'mc/edit-ends-of-lines)
(global-set-key (kbd "C-c c a") 'mc/edit-beginnings-of-lines)


;; Train myself to use M-f and M-b instead
(global-unset-key [M-left])
(global-unset-key [M-right])



(defun kill-back-to-indentation ()
  "Kill from point back to the first non-whitespace character on the line."
  (interactive)
  (let ((prev-pos (point)))
    (back-to-indentation)
    (kill-region (point) prev-pos)))

(global-set-key (kbd "S-<backspace>") 'kill-back-to-indentation)

;;----------------------------------------------------------------------------
;; Page break lines
;;----------------------------------------------------------------------------
(require-package 'page-break-lines)
(global-page-break-lines-mode)
(diminish 'page-break-lines-mode)

;;----------------------------------------------------------------------------
;; Shift lines up and down with M-up and M-down. When paredit is enabled,
;; it will use those keybindings. For this reason, you might prefer to
;; use M-S-up and M-S-down, which will work even in lisp modes.
;;----------------------------------------------------------------------------
(require-package 'move-dup)
;(global-set-key [M-up] 'md/move-lines-up)
;(global-set-key [M-down] 'md/move-lines-down)
;(global-set-key [M-S-up] 'md/move-lines-up)
;(global-set-key [M-S-down] 'md/move-lines-down)

(global-set-key (kbd "C-c d") 'md/duplicate-down)
(global-set-key (kbd "C-c D") 'md/duplicate-up)

;;----------------------------------------------------------------------------
;; Fix backward-up-list to understand quotes, see http://bit.ly/h7mdIL
;;----------------------------------------------------------------------------
(defun backward-up-sexp (arg)
  "Jump up to the start of the ARG'th enclosing sexp."
  (interactive "p")
  (let ((ppss (syntax-ppss)))
    (cond ((elt ppss 3)
           (goto-char (elt ppss 8))
           (backward-up-sexp (1- arg)))
          ((backward-up-list arg)))))

(global-set-key [remap backward-up-list] 'backward-up-sexp) ; C-M-u, C-M-up


;;----------------------------------------------------------------------------
;; Cut/copy the current line if no region is active
;;----------------------------------------------------------------------------
(require-package 'whole-line-or-region)
(whole-line-or-region-mode t)
(diminish 'whole-line-or-region-mode)
(make-variable-buffer-local 'whole-line-or-region-mode)

(defun suspend-mode-during-cua-rect-selection (mode-name)
  "Add an advice to suspend `MODE-NAME' while selecting a CUA rectangle."
  (let ((flagvar (intern (format "%s-was-active-before-cua-rectangle" mode-name)))
        (advice-name (intern (format "suspend-%s" mode-name))))
    (eval-after-load 'cua-rect
      `(progn
         (defvar ,flagvar nil)
         (make-variable-buffer-local ',flagvar)
         (defadvice cua--activate-rectangle (after ,advice-name activate)
           (setq ,flagvar (and (boundp ',mode-name) ,mode-name))
           (when ,flagvar
             (,mode-name 0)))
         (defadvice cua--deactivate-rectangle (after ,advice-name activate)
           (when ,flagvar
             (,mode-name 1)))))))

(suspend-mode-during-cua-rect-selection 'whole-line-or-region-mode)




(defun sanityinc/open-line-with-reindent (n)
  "A version of `open-line' which reindents the start and end positions.
If there is a fill prefix and/or a `left-margin', insert them
on the new line if the line would have been blank.
With arg N, insert N newlines."

  (interactive "*p")
  (let* ((do-fill-prefix (and fill-prefix (bolp)))
     (do-left-margin (and (bolp) (> (current-left-margin) 0)))
     (loc (point-marker))
     ;; Don't expand an abbrev before point.
     (abbrev-mode nil))
    (delete-horizontal-space t)
    (newline n)
    (indent-according-to-mode)
    (when (eolp)
      (delete-horizontal-space t))
    (goto-char loc)
    (while (> n 0)
      (cond ((bolp)
         (if do-left-margin (indent-to (current-left-margin)))
         (if do-fill-prefix (insert-and-inherit fill-prefix))))
      (forward-line 1)
      (setq n (1- n)))
    (goto-char loc)
    (end-of-line)
    (indent-according-to-mode)))

;;----------------------------------------------------------------------------
;; Random line sorting
;;----------------------------------------------------------------------------
(defun sort-lines-random (beg end)
  "Sort lines in region randomly."
  (interactive "r")
  (save-excursion
    (save-restriction
      (narrow-to-region beg end)
      (goto-char (point-min))
      (let ;; To make `end-of-line' and etc. to ignore fields.
          ((inhibit-field-text-motion t))
        (sort-subr nil 'forward-line 'end-of-line nil nil
                   (lambda (s1 s2) (eq (random 2) 0)))))))




(require-package 'highlight-escape-sequences)
(hes-mode)

(require-package 'guide-key)
(setq guide-key/guide-key-sequence '("C-x" "C-c" "C-x 4" "C-x 5" "C-c ;" "C-c ; f" "C-c ' f" "C-x n" "C-x C-r" "C-x r" "M-s" "C-h"))
(add-hook 'after-init-hook
          (lambda ()
            (guide-key-mode 1)
            (diminish 'guide-key-mode)))

;;----------------------------------------------------------------------------
;; More useful things
;;----------------------------------------------------------------------------
(set-default 'truncate-lines t)
(setq show-trailing-whitespace nil)
(setq site-lisp-path (file-name-as-directory (expand-file-name "site-lisp/" user-emacs-directory)))

(require-package 'fill-column-indicator)

;; ;; Global-FCI-mode can cause problems, like minibuffer with fci or information windows
;; ;; drawing incorrectly.
(define-globalized-minor-mode global-fci-mode fci-mode (lambda () (fci-mode 1)))
(defvar fci-mode nil)
;; (global-fci-mode 1)
(add-hook 'prog-mode-hook 'my/fci-mode-stuff)

(defun my/fci-mode-stuff ()
    (fci-mode 1)
    (setq fci-rule-color "dim gray")
    (setq fci-rule-width 2)
    (setq fci-rule-column 130)
    ;; Workaround because of implementation of show-trailing-whitespace https://github.com/alpaker/Fill-Column-Indicator/blob/master/fill-column-indicator.el
    (setq whitespace-style '(face trailing))
    ;; fci-mode not compatible with non-nil values of hl-line-sticky-flag
    (setq hl-line-sticky-flag nil))

;; (set-fill-column 130)
;; (global-whitespace-mode)
;;
;; Word Count Mode with Goals. Can be activated, Run in Modeline
(require-package 'wc-mode)
; hideshowvis for hs-mode
;(require-package 'hideshowvis)
(dolist (hook (list 'emacs-lisp-mode-hook
                    'java-mode-hook
                    'lisp-mode-hook
                    'perl-mode-hook
                    'hs-mode-hook
                    'c-mode-common-hook))
  (add-hook hook 'hs-minor-mode))
;; From EmacsWiki expand hidden block when searching for line
(defadvice goto-line (after expand-after-goto-line
                            activate compile)
  "hideshow-expand affected block when using goto-line in a collapsed buffer"
  (save-excursion
    (hs-show-block)))

;; Echo keystrokes
(setq echo-keystrokes 0.1)

;; Copy/Paste behaviour. Use X clipboard.
(setq x-select-enable-primary t)
(setq x-select-enable-clipboard nil)

;; Emacs Scroll like any other editor kaka pedo culo pis
(setq scroll-step            1
      scroll-conservatively  10000)
;; Show line numbers when using go-to line when line numbers are not enabled
;; Stolen from http://whattheemacsd.com/key-bindings.el-01.html
(global-set-key [remap goto-line] 'goto-line-with-feedback)

(defun goto-line-with-feedback ()
  "Show line numbers temporarily, while prompting for the line number input"
  (interactive)
  (unwind-protect
      (progn
        (linum-mode 1)
        (goto-line (read-number "Goto line: ")))
    (linum-mode -1)))

;; Delete trailing whitespace before saving fil
(add-hook 'before-save-hook 'delete-trailing-whitespace)

;;----------------------------------------------------------------------------
;; Key definitions
;;----------------------------------------------------------------------------
(global-set-key (kbd "RET") 'newline-and-indent)
(global-set-key (kbd "s-<return>") 'sanityinc/newline-at-end-of-line)

(global-set-key (kbd "C-<up>") 'backward-paragraph)
(global-set-key (kbd "C-<down>") 'forward-paragraph)
(global-set-key (kbd "C-<left>") 'backward-word)
(global-set-key (kbd "C-<right>") 'forward-word)

(global-set-key [(s-backspace)] 'undo)
(global-set-key [(s-S-backspace)] 'redo) ;Meta -Shift-Backspace
(global-set-key (kbd "C-o") 'sanityinc/open-line-with-reindent)

;; Mouse horizontal scrolling (in case your mouse has horizontal scroll)
(global-set-key [mouse-6] 'scroll-right)
(global-set-key [mouse-7] 'scroll-left)

;; Sometimes my laptop can't autoindent
(global-set-key (kbd "C-M-º") 'indent-region)

;; hs-mode key bindings I like
(global-set-key (kbd "M-+") 'hs-show-block)
(global-set-key (kbd "M--") 'hs-hide-block)

;; Join lines on M-j
(global-set-key (kbd "M-j") (lambda () (interactive) (join-line -1)))

;; Focus mode toggle
(global-set-key (kbd "s-º") (lambda () (interactive) (focus-mode 'toggle)))

;; Browse kill ring
(global-set-key (kbd "C-c k") 'browse-kill-ring)

;; Highlight symbol
(global-set-key (kbd "M-s-h") 'highlight-symbol)

;; CUA for rectangular selections
(global-set-key (kbd "M-RET") 'cua-rectangle-mark-mode)

;; Smarter move to the beginning of the line
;; remap C-a or to `smarter-move-beginning-of-line'
(global-set-key [remap move-beginning-of-line]
                'sacha/smarter-move-beginning-of-line)

(myemacs/elapsed-time)
(provide 'init-editing-utils)

Ahora unas cuantas explicaciones sobre lo que se ha hecho en este archivo:

Definimos algunas funciones nuevas con utilidades:

También definimos algunos valores de configuración como por ejemplo:

Autocompletado


Algo básico en cualquier editor de código es que éste nos ayude a escribir. El desarrollo de un programa es algo muy duro para nosotros (que sea divertido es otra cosa) y actualmente tenemos ordenadores muy potentes por lo que podemos aprovechar esa potencia para evitarnos buscar y tener que recordar miles de cosas o, aunque las recordemos, al menos que sea rápido escribirlas, porque a veces hay funciones, variables o tipos con nombres muy largos que tenemos que escribir una y otra vez.

Así que, es importante contar con un buen sistema de autocompletado. En Emacs, disponemos, entre otros de auto-complete-mode y company-mode, al menos son los más grandes. A mí me gusta auto-complete-mode y este será mi fichero init-auto-complete.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
46
47
48
49
50
51
52
;; Auto-complete basic configuration.
;; We'll find more specific configuration in language files
(require 'auto-complete)
(require 'auto-complete-config)
(global-auto-complete-mode)
(setq ac-comphist-file (expand-file-name
              "ac-comphist.dat" user-emacs-directory))

;; (add-to-list 'ac-dictionary-directories (expand-file-name
;;                   "~/.emacs.d_new/elpa/auto-complete-20170124.1845/dict"))

;;a word must be 3 chars long before completition begins
(setq-default ac-auto-start 2)
(set-default 'ac-sources '(ac-source-abbrev ac-source-words-in-same-mode-buffers ac-source-dictionary ac-source-words-in-buffer))
(setq-default ac-ignore-case 'smart)
;; Do What I Mean mode - Changes TAB behaviour depending on context
(setq-default ac-dwim t)
(setq ac-quick-help-delay 1)
;; Limit candidate limit
(setq ac-candidate-limit 400)
;; Allow searches
(setq ac-use-menu-map t)
(setq ac-candidate-menu-min 0)

(defun ac-emacs-lisp-mode-setup ()
  (setq ac-sources (append
                    '(ac-source-dictionary
                      ac-source-features
                      ac-source-functions
                      ac-source-variables
                      ac-source-symbols)
                                        ac-sources))

  (setq ac-omni-completion-sources
        '(("\\<featurep\s+'" ac+-source-elisp-features)
          ("\\<require\s+'"  ac+-source-elisp-features)
          ("\\<load\s+""    ac-source-emacs-lisp-features))))

(add-hook 'emacs-lisp-mode-hook 'ac-emacs-lisp-mode-setup)

(defun ac-eshell-mode-setup ()
    (auto-complete-mode 1)
  (setq ac-sources (append '(ac-source-abbrev
                             ac-source-words-in-same-mode-buffers
                             ac-source-files-in-current-dir)
                           ac-sources)))

(add-hook 'eshell-mode-hook 'ac-eshell-mode-setup)


(myemacs/elapsed-time)
(provide 'init-auto-complete)

En este fichero defino sólo la parte más básica. Cada lenguaje irá introduciendo sus propias formas de autocompletado. Por ejemplo C y C++ explorarán archivos fuente con Semantic y lo mezclarán con información sobre las bibliotecas; Lisp tendrá muchas funciones y símbolos definidas, otros lenguajes utilizarán parte de sus manuales para completar palabras, etc. Pero en este fichero sólo incluyo la configuración por defecto de auto-complete, lo activo globalmente, estblezco el fichero de historial y defino algunas preferencias:

En el próximo post veremos características personalizadas para lenguajes de programación. En los lenguajes incluiré C, C++, PHP, Python, Javascript, Ruby, HTML, SQL, CSS, LUA y alguno más.

Teclas rápidas


Estas son algunas de las teclas rápidas que se han establecido en este post. En el último post de la serie haré un resumen de todas las teclas rápidas, para tenerlas a modo de chuleta rápida:

Teclas para trabajar con varios cursores a la vez

Los cursores múltiples nos permiten editar varios lugares dentro de un buffer. Son muy útiles cuando insertamos o eliminamos trazas, cuando tenemos que escribir o borrar el mismo texto en varias regiones del código sin tener que ir al buscar y reemplazar varias veces o simplemente cuando queremos sorprender a las visitas o a los que nos miran por encima del hombro cuando estamos trabajando.
En mi configuración de Emacs podemos utilizar las siguientes teclas (algunas de ellas sacadas de la página oficial de la extensión multiple-cursors de Emacs).
Primero debemos seleccionar una región, puede ser una palabra o un fragmento, ya lo hagamos con el ratón o con C-SPC. Luego pulsamos:

También podemos colocar cursores con el ratón si pulsamos s- o lo que es lo mismo la tecla meta o tecla Windows y luego hacer click en algún punto del buffer. Cuando tengamos los cursores podemos escribir cualquier texto y se replicará en cada uno de los cursores, y para insertar una nueva línea podemos pulsar C-j. Los cursores desaparecerán al pulsar Enter o C-g.

Otras opciones que tenemos disponibles son:

Foto principal: unsplash-logoCaspar Rubin

También podría interesarte....