Publi

Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (III – Lenguajes de programación)

Lenguajes de programación en Emacs

Seguimos con la serie de posts sobre mi configuración de Emacs. Hoy toca el turno de los lenguajes de programación y de cómo tengo Emacs configurado para soportarlos. En concreto, mi trabajo diario se centra en C, C++, PHP, Python y Bash, aunque me preocupo por el soporte de Emacs Lisp y de algún que otro lenguaje que utilizo de forma esporádica.

Os dejo aquí enlaces a las dos partes anteriores:

Como habéis podido observar tengo la configuración dividida en varios archivos. En la configuración particular de cada lenguaje tendremos cosas como el autocompletado, coloreado, anidado, navegación en el código o plugins que necesitamos activar o desactivar en algunos casos. Todo ello, incorporado en Emacs para que funcione de forma automática.

Compilaciones

Aquí defino algunas directivas para poder lanzar la compilación de un programa. Unas funciones muy sencillas para poder lanzar un Make sobre el código que estoy editando.

init-compile.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
;; From: https://github.com/purcell/emacs.d/blob/master/lisp/init-compile.el
(setq-default compilation-scroll-output t)

(require-package 'alert)

;; Customize `alert-default-style' to get messages after compilation

(after-load 'compile
  (add-hook 'compilation-finish-functions
            'sanityinc/alert-after-compilation-finish))

(after-load 'compile
  (defadvice compilation-start (after sanityinc/save-compilation-buffer activate)
    "Save the compilation buffer to find it later."
    (setq sanityinc/last-compilation-buffer next-error-last-buffer))

  (defadvice recompile (around sanityinc/find-prev-compilation (&optional edit-command) activate)
    "Find the previous compilation buffer, if present, and recompile there."
    (if (and (null edit-command)
             (not (derived-mode-p 'compilation-mode))
             sanityinc/last-compilation-buffer
             (buffer-live-p (get-buffer sanityinc/last-compilation-buffer)))
        (with-current-buffer sanityinc/last-compilation-buffer
          ad-do-it)
      ad-do-it)))

(defadvice shell-command-on-region
    (after sanityinc/shell-command-in-view-mode
           (start end command &optional output-buffer &rest other-args)
           activate)
  "Put "*Shell Command Output*" buffers into view-mode."
  (unless output-buffer
    (with-current-buffer "*Shell Command Output*"
      (view-mode 1))))


(after-load 'compile
  (require 'ansi-color)
  (defun sanityinc/colourise-compilation-buffer ()
    (when (eq major-mode 'compilation-mode)
      (ansi-color-apply-on-region compilation-filter-start (point-max))))
  (add-hook 'compilation-filter-hook 'sanityinc/colourise-compilation-buffer))

;; Key bindings
(global-set-key [S-f6] 'recompile)
(global-set-key [f6] 'compile)


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

Lenguajes derivados de C

En Emacs CC-Mode, gestiona los buffers escritos en C. Además, muchos modos para lenguajes parecidos a C (C++, PHP, Javascript, Java, y más) heredan partes de este modo, por lo que es uno de los esenciales.

init-cmode.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
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
(require 'compile)

;; C Compilation
(add-hook 'c-mode-hook
      (lambda ()
        (unless (file-exists-p "Makefile")
          (set (make-local-variable 'compile-command)
           ;; emulate make's .c.o implicit pattern rule, but with
           ;; different defaults for the CC, CPPFLAGS, and CFLAGS
           ;; variables:
           ;; $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
           (let ((file (file-name-nondirectory buffer-file-name)))
             (format "%s -c -o %s.o %s %s %s"
                 (or (getenv "CC") "gcc")
                 (file-name-sans-extension file)
                 (or (getenv "CPPFLAGS") "-DDEBUG=9")
                 (or (getenv "CFLAGS") "-ansi -pedantic -Wall -g")
                 file))))))

(add-hook 'c++-mode-hook
      (lambda ()
        (unless (file-exists-p "Makefile")
          (set (make-local-variable 'compile-command)
           ;; emulate make's .c.o implicit pattern rule, but with
           ;; different defaults for the CC, CPPFLAGS, and CFLAGS
           ;; variables:
           ;; $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
           (let ((file (file-name-nondirectory buffer-file-name)))
             (format "%s -c -o %s.o %s %s %s"
                 (or (getenv "CXX") "g++")
                 (file-name-sans-extension file)
                 (or (getenv "CPPFLAGS") "-DDEBUG=9")
                 (or (getenv "CFLAGS") "-ansi -pedantic -Wall -g")
                 file))))))

;; Use C++ Mode if a File starts with // or #include
(add-to-list 'magic-fallback-mode-alist '("^// " . c++-mode))
(add-to-list 'magic-fallback-mode-alist '("^#include" . c++-mode))

;;----------------------------------------------------------------------------
;; General for C-like languages
;;----------------------------------------------------------------------------

;; Remove system recursive search (it's veeeeeeeeeery slow)
(if (featurep 'semantic)
    (setq-mode-local c-mode
             semanticdb-find-default-throttle
             '(file project local unloaded recursive)))

(setq-default c-basic-offset 2)
(setq-default tab-width 2)
(setq-default indent-tabs-mode 't)

(defun my/c-mode-hook ()
  (require 'auto-complete-c-headers)
    (setq achead:include-directories
                (append '("/usr/lib/gcc/x86_64-linux-gnu/5/include"
                                    "/usr/local/include"
                                    "/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed"
                                    "/usr/include/x86_64-linux-gnu"
                                    "/usr/include")
                                achead:include-directories))
    (global-set-key [(shift return)] 'c-indent-new-comment-line)
    (add-to-list 'ac-sources 'ac-source-semantic2)
    (add-to-list 'ac-sources 'ac-source-c-headers)

    ;; Sets configuration for current file
    (setq c-basic-offset 2)
    (setq tab-width 2)
    (setq indent-tabs-mode 't)

    ;; Indentation correct
    ;; (c-set-offset 'arglist-cont-nonempty 'tab-width)
    ;; (c-set-offset 'arglist-cont-nonempty tab-width)
    (c-set-offset 'arglist-intro 't)
    (c-set-offset 'arglist-close 0)
    (local-set-key "\C-ch" 'eassist-switch-h-cpp)
    (local-set-key "\C-ce" 'eassist-list-methods)
  ;; References of function
  (local-set-key "\C-c\C-r" 'semantic-symref)

  )
(defun my/cpp-mode-hook ()
    (my/c-mode-hook)
    (setq achead:include-directories
                (append '("/usr/include/c++/5"
                                     "/usr/include/x86_64-linux-gnu/c++/5"
                                     "/usr/include/c++/5/backward")
                                achead:include-directories))
    )

(add-hook 'c++-mode-hook 'my/cpp-mode-hook)
(add-hook 'c-mode-hook 'my/c-mode-hook)

;; Derived from Alex Ott work
(defun my/ac-semantic-candidates ()
  (let* ((a (semantic-analyze-current-context ac-point))
         (tags (semantic-analyze-possible-completions a)))
    (cl-loop for tag in tags
             for class = (semantic-tag-class tag)
             for name = (semantic-format-tag-name tag nil nil)
             for type = (when (member class '(function variable type))
                          (semantic-format-tag-type tag nil))
             for args = (when (member class '(function type))
                          (semantic--format-tag-arguments
                           (if (eq class 'function)
                               (semantic-tag-function-arguments tag)
                             (list ""))
                           #'semantic-format-tag-prototype
                           nil))
                         for documentt = (semantic-documentation-for-tag tag)
                         for document = (if documentt documentt "No documentation found.")

             for summary = (if (eq class 'function)
                               (format "(%s) : %s\n\n ------------ \n%s" args type document)
                             (format "Ret: %s %s\n\n ------------ \n%s" class type document))

             collect
             (popup-make-item name :summary (symbol-name class) :document summary)

                         )))


(ac-define-source semantic2
  '((available . (require 'semantic/ia nil t))
    (candidates . my/ac-semantic-candidates)
    (prefix . cc-member)
        (symbol . "f")
    (requires . 0)))

(add-hook 'c++-mode-hook (lambda () (setq comment-start "/* "
                      comment-end   "*/")))

(add-hook 'c-mode-hook (lambda () (setq comment-start "/* "
                    comment-end   "*/")))

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

Lo primero que configuramos es la compilación. Permitiendo compilar un programa rápidamente utilizando el comando gcc o g++, en modo depuración, lo cual es muy útil para hacer compilaciones rápidas. También se utiliza el magic-fallback-mode-alist para que los archivos que comiencen por dos barras (//) o #include automáticamente utilicen el modo C++ sea cual sea su extensión. Esto es interesante, cuando estoy editando archivos de Arduino, por ejemplo.

En C-mode podemos configurar casi cualquier aspecto acerca de la colocación del código y de cómo lo indentamos. En principio podemos configurar modos de indentación predefinidos con:

1
(setq c-default-style "linux")

Donde en lugar de linux, podemos escribir gnu, bsd, k&r, stroustrup, y algunos más. Si queremos ver el listado completo, basta con hacer M-x (print c-style-alist). Aunque también podemos configurar cada uno de los elementos por separado, como es mi caso. Aunque hay más elementos para configurar, me basta con indent-tabs-mode, tab-width y c-basic-offset.

Ahora, lo más interesante para mí es el auto-completado. Para ello utilizo autocomplete y semantic. En este caso, para que la información salga mejor en semantic utilizo una función para generar el autocompletado. Es cierto que hay varias formas más de generarlo, mucho más rápidas que tirar de Elisp, pero no funciona excesivamente lento (excepto cuando hay muchos archivos en el proyecto para analizar).

init-php.el

En este archivo trataremos la configuración específica para PHP:

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
(require-package 'php-mode)

(when (maybe-require-package 'php-mode)
  (maybe-require-package 'smarty-mode))

;; From EmacsWiki: https://www.emacswiki.org/emacs/PhpMode
(defun my/php-symbol-lookup ()
  (interactive)
  (let ((symbol (symbol-at-point)))
    (if (not symbol)
        (message "No symbol at point.")

      (browse-url (concat "http://php.net/manual-lookup.php?pattern="
                          (symbol-name symbol))))))


(defun my/php-function-lookup ()
  (interactive)
  (let* ((function (symbol-name (or (symbol-at-point)
                                    (error "No function at point."))))
         (buf (url-retrieve-synchronously (concat "http://php.net/manual-lookup.php?pattern=" function))))
    (with-current-buffer buf
      (goto-char (point-min))
        (let (desc)
          (when (re-search-forward "<div class="methodsynopsis dc-description">\\(\\(.\\|\n\\)*?\\)</div>" nil t)
            (setq desc
              (replace-regexp-in-string
                " +" " "
                (replace-regexp-in-string
                  "\n" ""
                  (replace-regexp-in-string "<.*?>" "" (match-string-no-properties 1)))))

            (when (re-search-forward "<p class="para rdfs-comment">\\(\\(.\\|\n\\)*?\\)</p>" nil t)
              (setq desc
                    (concat desc "\n\n"
                            (replace-regexp-in-string
                             " +" " "
                             (replace-regexp-in-string
                              "\n" ""
                              (replace-regexp-in-string "<.*?>" "" (match-string-no-properties 1))))))))

          (if desc
              (message desc)
            (message "Could not extract function info. Press C-F1 to go the description."))))
    (kill-buffer buf)))

(require-package 'ac-php)

(add-hook 'php-mode-hook 'my/php-mode-stuff)

(defun my/php-mode-stuff ()
  (local-set-key (kbd "<f1>") 'my/php-function-lookup)
  (local-set-key (kbd "C-<f1>") 'my/php-symbol-lookup)
    ;; New versions of PHP have this :)
    (php-enable-psr2-coding-style)
    (fci-mode 0)
    (auto-complete-mode t)
    (require 'ac-php)
    (setq ac-sources  '(ac-source-dictionary ac-source-abbrev ac-source-php ) )
    (ac-php-core-eldoc-setup ) ;enable eldoc
    (define-key php-mode-map  (kbd "C-]") 'ac-php-find-symbol-at-point)   ;goto define
    (define-key php-mode-map  (kbd "C-t") 'ac-php-location-stack-back   ) ;go back
    )

(add-hook 'php-mode-hook 'my/php-mode-stuff)

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

En este fichero se definirán dos funciones para consultar ayuda en línea: my/php-function-lookup y my/php-symbol-lookup. Además, estableceremos el estilo de codificación psr2 y algunas teclas rápidas que vemos al final del post.

init-python.el

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;;----------------------------------------------------------------------------
;; Python Mode
;;----------------------------------------------------------------------------
(require-package 'jedi)
(setq auto-mode-alist
      (append '(("SConstruct\'" . python-mode)
        ("SConscript\'" . python-mode))
              auto-mode-alist))

(require-package 'pip-requirements)
(defun my/python-mode-stuff ()
    ;; Jedi makes everything a lot easier for everybody!
    (jedi:setup)
    (define-key python-mode-map (kbd "C-]") 'jedi:goto-definition)   ;goto define
  (local-set-key (kbd "<f1>") 'jedi:show-doc)
    (setq jedi:complete-on-dot t)                 ; optional
    )
(add-hook 'python-mode-hook 'my/python-mode-stuff)

(myemacs/elapsed-time)

(provide 'init-python)

Mi configuración para Python es reducida, aunque podemos hacer muchas cosas nada más activando el modo jedi para Python. Actúa sobre el auto-completado, aunque tiene algunas utilidades de análisis de código que harán nuestra programación más amena y productiva.

init-javascript.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
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
(require-package 'json-mode)
(require-package 'js2-mode)
(require-package 'coffee-mode)

(defcustom preferred-javascript-mode
  (first (remove-if-not #'fboundp '(js2-mode js-mode)))
  "Javascript mode to use for .js files."
  :type 'symbol
  :group 'programming
  :options '(js2-mode js3-mode js-mode))

(defconst preferred-javascript-indent-level 2)

;; Need to first remove from list if present, since elpa adds entries too, which
;; may be in an arbitrary order
(eval-when-compile (require 'cl))
(setq auto-mode-alist (cons `("\\.\\(js\\|es6\\)\\(\\.erb\\)?\'" . ,preferred-javascript-mode)
                            (loop for entry in auto-mode-alist
                                  unless (eq preferred-javascript-mode (cdr entry))
                                  collect entry)))


(add-auto-mode 'js-mode "\\.json\'")

;; js2-mode

;; Change some defaults: customize them to override
(setq-default js2-basic-offset preferred-javascript-indent-level
                            js2-use-font-lock-faces t
                            js2-indent-on-enter-key t
                            js-auto-indent-p t
              js2-bounce-indent-p nil)
(after-load 'js2-mode
  ;; Disable js2 mode's syntax error highlighting by default...
  (setq-default js2-mode-show-parse-errors nil
                js2-mode-show-strict-warnings nil)
  ;; ... but enable it if flycheck can't handle javascript
  ;(autoload 'flycheck-get-checker-for-buffer "flycheck")
  (defun sanityinc/disable-js2-checks-if-flycheck-active ()
    (unless (flycheck-get-checker-for-buffer)
      (set (make-local-variable 'js2-mode-show-parse-errors) t)
      (set (make-local-variable 'js2-mode-show-strict-warnings) t)))
  (add-hook 'js2-mode-hook 'sanityinc/disable-js2-checks-if-flycheck-active)

  (add-hook 'js2-mode-hook (lambda () (setq mode-name "JS2")))

  (after-load 'js2-mode
    (js2-imenu-extras-setup)))

;; js3-mode
(add-hook 'js3-mode-hook '(lambda () (setq mode-name "JS3")))
(setq js3-auto-indent-p t
      js3-enter-indents-newline t
      js3-indent-on-enter-key t
            js3-indent-level preferred-javascript-indent-level)

;; js-mode
(setq-default js-indent-level preferred-javascript-indent-level)

(add-to-list 'interpreter-mode-alist (cons "node" preferred-javascript-mode))

;; Javascript nests {} and () a lot, so I find this helpful

(when (and (executable-find "ag")
           (maybe-require-package 'xref-js2))
  (after-load 'js2-mode
    (define-key js2-mode-map (kbd "M-.") nil)
    (add-hook 'js2-mode-hook
              (lambda () (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t)))))

;;; Coffeescript

(after-load 'coffee-mode
  (setq coffee-js-mode preferred-javascript-mode
        coffee-tab-width preferred-javascript-indent-level))

(when (fboundp 'coffee-mode)
  (add-to-list 'auto-mode-alist '("\\.coffee\\.erb\'" . coffee-mode)))

;; standard javascript-mode
(setq javascript-indent-level preferred-javascript-indent-level)

(add-to-list 'interpreter-mode-alist (cons "nodejs" preferred-javascript-mode))

;; ---------------------------------------------------------------------------
;; Run and interact with an inferior JS via js-comint.el
;; ---------------------------------------------------------------------------
;; ---------------------------------------------------------------------------
;; Run and interact with an inferior JS via js-comint.el
;; ---------------------------------------------------------------------------

(setq inferior-js-program-command "nodejs")

(defvar inferior-js-minor-mode-map (make-sparse-keymap))
;; (define-key inferior-js-minor-mode-map "\C-x\C-e" 'js-send-last-sexp)
;; (define-key inferior-js-minor-mode-map "\C-\M-x" 'js-send-last-sexp-and-go)
;; (define-key inferior-js-minor-mode-map "\C-cb" 'js-send-buffer)
;; (define-key inferior-js-minor-mode-map "\C-c\C-b" 'js-send-buffer-and-go)
;; (define-key inferior-js-minor-mode-map "\C-cl" 'js-load-file-and-go)

(define-minor-mode inferior-js-keys-mode
  "Bindings for communicating with an inferior js interpreter."
  nil " InfJS" inferior-js-minor-mode-map)

(dolist (hook '(js2-mode-hook js3-mode-hook js-mode-hook))
    (add-hook hook 'inferior-js-keys-mode))

;; ---------------------------------------------------------------------------
;; Alternatively, use skewer-mode
;; ---------------------------------------------------------------------------

(when (maybe-require-package 'skewer-mode)
  (after-load 'skewer-mode
    (add-hook 'skewer-mode-hook
              (lambda () (inferior-js-keys-mode -1)))))


(provide 'init-javascript)

Con este fichero configuramos tanto Javascript, ficheros Json y Coffeescript. Hay algunas teclas comentadas porque realmente no las utilizo, pero podríamos utilizar js-comint-mode para ejecutar código javascript en un buffer mientras editamos en otro.

nxml-mode.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
; From https://github.com/purcell/emacs.d/blob/master/lisp/init-nxml.el
(add-auto-mode
 'nxml-mode
 (concat "\\."
         (regexp-opt
          '("xml" "xsd" "sch" "rng" "xslt" "svg" "rss"
            "gpx" "tcx" "plist"))
         "\'"))
(setq magic-mode-alist (cons '("<\\?xml " . nxml-mode) magic-mode-alist))
(fset 'xml-mode 'nxml-mode)
(add-hook 'nxml-mode-hook (lambda ()
                            (set (make-local-variable 'ido-use-filename-at-point) nil)
                                                        (setq nxml-slash-auto-complete-flag t)
                                                        (auto-complete-mode t)
                                                        (local-set-key (kbd "RET") 'newline-and-indent)))


;; See: http://sinewalker.wordpress.com/2008/06/26/pretty-printing-xml-with-emacs-nxml-mode/
(defun sanityinc/pp-xml-region (beg end)
  "Pretty format XML markup in region. The function inserts
linebreaks to separate tags that have nothing but whitespace
between them.  It then indents the markup by using nxml's
indentation rules."

  (interactive "r")
  (unless (use-region-p)
    (setq beg (point-min)
          end (point-max)))
  ;; Use markers because our changes will move END
  (setq beg (set-marker (make-marker) begin)
        end (set-marker (make-marker) end))
  (save-excursion
    (goto-char beg)
    (while (search-forward-regexp "\>[ \\t]*\<" end t)
      (backward-char) (insert "\n"))
    (nxml-mode)
    (indent-region begin end)))

;;----------------------------------------------------------------------------
;; Integration with tidy for html + xml
;;----------------------------------------------------------------------------
(require-package 'tidy)
(add-hook 'nxml-mode-hook (lambda () (tidy-build-menu nxml-mode-map)))

(defun sanityinc/tidy-buffer-xml (beg end)
  "Run "tidy -xml" on the region from BEG to END, or whole buffer."
  (interactive "r")
  (unless (use-region-p)
    (setq beg (point-min)
          end (point-max)))
  (shell-command-on-region beg end "tidy -xml -q -i" (current-buffer) t "*tidy-errors*" t))

(defun jta/reformat-xml ()
  "Reformats xml to make it readable (respects current selection)."
  (interactive)
  (save-excursion
    (let ((beg (point-min))
          (end (point-max)))
      (if (and mark-active transient-mark-mode)
          (progn
            (setq beg (min (point) (mark)))
            (setq end (max (point) (mark))))
        (widen))
      (setq end (copy-marker end t))
      (goto-char beg)
      (while (re-search-forward ">\\s-*<" end t)
        (replace-match ">\n<" t t))
      (goto-char beg)
      (indent-region beg end nil))))


(provide 'init-nxml)

Ahora le toca el turno a los lenguajes XML y derivados. En los que, cuando los editamos podemos tener algunas ventajas, como por ejemplo, ponerlos bonitos. Aunque hay tres funciones para ello, sanityinc/pp-xml-region, sanityinc/tidy-buffer-xml y jta/reformat-xml cada una tiene sus pros y sus contras. Aunque la única que no me ha dejado tirado nunca es jta/reformat-xml aunque puede llegar a ser algo lenta.

init-ruby.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
; From https://github.com/purcell/emacs.d/blob/master/lisp/init-ruby-mode.el
;;; Basic ruby setup
(require-package 'ruby-mode)
(require-package 'ruby-hash-syntax)

(add-auto-mode 'ruby-mode
               "Rakefile\'" "\\.rake\'" "\\.rxml\'"
               "\\.rjs\'" "\\.irbrc\'" "\\.pryrc\'" "\\.builder\'" "\\.ru\'"
               "\\.gemspec\'" "Gemfile\'" "Kirkfile\'")
(add-auto-mode 'conf-mode "Gemfile\\.lock\'")

(setq ruby-use-encoding-map nil)

(after-load 'ruby-mode
  (define-key ruby-mode-map (kbd "TAB") 'indent-for-tab-command)

  ;; Stupidly the non-bundled ruby-mode isn't a derived mode of
  ;; prog-mode: we run the latter's hooks anyway in that case.
  (add-hook 'ruby-mode-hook
            (lambda ()
              (unless (derived-mode-p 'prog-mode)
                (run-hooks 'prog-mode-hook)))))

(add-hook 'ruby-mode-hook 'subword-mode)

(after-load 'page-break-lines
  (push 'ruby-mode page-break-lines-modes))

(require-package 'rspec-mode)


;;; Inferior ruby
(require-package 'inf-ruby)



;;; Ruby compilation
(require-package 'ruby-compilation)

(after-load 'ruby-mode
  (let ((m ruby-mode-map))
    (define-key m [S-f7] 'ruby-compilation-this-buffer)
    (define-key m [f7] 'ruby-compilation-this-test)))

(after-load 'ruby-compilation
  (defalias 'rake 'ruby-compilation-rake))



;;; Robe
(require-package 'robe)
(after-load 'ruby-mode
  (add-hook 'ruby-mode-hook 'robe-mode))
(after-load 'company
  (dolist (hook '(ruby-mode-hook inf-ruby-mode-hook html-erb-mode-hook haml-mode))
    (add-hook hook
              (lambda () (sanityinc/local-push-company-backend 'company-robe)))))



;; Customise highlight-symbol to not highlight do/end/class/def etc.
(defun sanityinc/suppress-ruby-mode-keyword-highlights ()
  "Suppress highlight-symbol for do/end etc."
  (set (make-local-variable 'highlight-symbol-ignore-list)
       (list (concat "\\_<" (regexp-opt '("do" "end")) "\\_>"))))
(add-hook 'ruby-mode-hook 'sanityinc/suppress-ruby-mode-keyword-highlights)

Aunque no soy muy de Ruby, alguna vez he tenido que editar algún código y esta configuración no me ha ido mal.

init-sql.el

Del mismo modo que antes, incluyo init-sql.el para trabajar con archivos SQL:

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
; From https://github.com/purcell/emacs.d/blob/master/lisp/init-sql.el
(require-package 'sql-indent)
(after-load 'sql
  (require 'sql-indent))

(after-load 'sql
  ;; sql-mode pretty much requires your psql to be uncustomised from stock settings
  (push "--no-psqlrc" sql-postgres-options))

(defun sanityinc/pop-to-sqli-buffer ()
  "Switch to the corresponding sqli buffer."
  (interactive)
  (if sql-buffer
      (progn
        (pop-to-buffer sql-buffer)
        (goto-char (point-max)))
    (sql-set-sqli-buffer)
    (when sql-buffer
      (sanityinc/pop-to-sqli-buffer))))

(after-load 'sql
  (define-key sql-mode-map (kbd "C-c C-z") 'sanityinc/pop-to-sqli-buffer)
  (when (package-installed-p 'dash-at-point)
    (defun sanityinc/maybe-set-dash-db-docset ()
      (when (eq sql-product 'postgres)
        (set (make-local-variable 'dash-at-point-docset) "psql")))

    (add-hook 'sql-mode-hook 'sanityinc/maybe-set-dash-db-docset)
    (add-hook 'sql-interactive-mode-hook 'sanityinc/maybe-set-dash-db-docset)
    (defadvice sql-set-product (after set-dash-docset activate)
      (sanityinc/maybe-set-dash-db-docset))))

(setq-default sql-input-ring-file-name
              (expand-file-name ".sqli_history" user-emacs-directory))

;; See my answer to https://emacs.stackexchange.com/questions/657/why-do-sql-mode-and-sql-interactive-mode-not-highlight-strings-the-same-way/673
(defun sanityinc/font-lock-everything-in-sql-interactive-mode ()
  (unless (eq 'oracle sql-product)
    (sql-product-font-lock nil nil)))
(add-hook 'sql-interactive-mode-hook 'sanityinc/font-lock-everything-in-sql-interactive-mode)


(after-load 'page-break-lines
  (push 'sql-mode page-break-lines-modes))

(provide 'init-sql)

Otros lenguajes init-langs.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
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
;;----------------------------------------------------------------------------
;; General for all languages
;;----------------------------------------------------------------------------
;; Useful for editing compressed files transparently
(auto-compression-mode t)

; From https://github.com/purcell/emacs.d/blob/master/lisp/init-markdown.el
;;----------------------------------------------------------------------------
;; Markdown mode
;;----------------------------------------------------------------------------
(when (maybe-require-package 'markdown-mode)
  (after-load 'whitespace-cleanup-mode
    (push 'markdown-mode whitespace-cleanup-mode-ignore-modes)))

;;----------------------------------------------------------------------------
;; CSV Mode
;;----------------------------------------------------------------------------

(require-package 'csv-mode)
(require-package 'csv-nav)

(add-auto-mode 'csv-mode "\\.[Cc][Ss][Vv]\'")

(setq csv-separators '("," ";" "|" " "))

;;----------------------------------------------------------------------------
;; HTML Mode
;;----------------------------------------------------------------------------
(require-package 'tidy)
(add-hook 'html-mode-hook (lambda () (tidy-build-menu html-mode-map)))

(require-package 'tagedit)
(after-load 'sgml-mode
  (tagedit-add-paredit-like-keybindings)
  (add-hook 'sgml-mode-hook (lambda () (tagedit-mode 1))))

(add-auto-mode 'html-mode "\\.\\(jsp\\|tmpl\\)\'")

;;----------------------------------------------------------------------------
;; CSS Mode
;;----------------------------------------------------------------------------
(setq-default cssm-indent-level 4)
(setq-default cssm-newline-before-closing-bracket t)
(setq-default cssm-indent-function #'cssm-c-style-indenter)
;; (setq-default scss-sass-command '/usr/local/bin/sass)
(setq-default cssm-mirror-mode nil)

;;----------------------------------------------------------------------------
;; Web mode
;;----------------------------------------------------------------------------
(require-package 'web-mode)
(add-hook 'web-mode-hook (lambda () ((fci-mode 0))))

(add-to-list 'auto-mode-alist '("\\.phtml\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tpl\\.php\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.[agj]sp\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.as[cp]x\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.mustache\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.djhtml\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.html?\'" . web-mode))

;;----------------------------------------------------------------------------
;; Multiple major modes
;;----------------------------------------------------------------------------
(require-package 'mmm-mode)
(require 'mmm-auto)
(setq mmm-global-mode 'buffers-with-submode-classes)
(setq mmm-submode-decoration-level 2)

;;----------------------------------------------------------------------------
;; Some more mode languages
;;----------------------------------------------------------------------------
(require-package 'gnuplot)
(require-package 'lua-mode)
(require-package 'ansible)
(require-package 'elf-mode)

(provide 'init-langs)

Aquí vemos cómo se comportará Emacs con archivos HTML, XML, lua, scripts para ansible, gnuplot, Markdown y algunos más. Eso sí, una cosa que me encanta, auto-compression-mode, que es capaz de utilizar archivos comprimidos de forma transparente para nosotros. Emacs se encarga de comprimir y descomprimir cuando lo crea necesario.

Teclas rápidas

En este post he añadido algunas combinaciones de teclas más a mi Emacs, aquí tenemos un resumen:

  • F6: Compilar el código actual.
  • Shift+F6: Recompilar el código actual. Útil para utilizar una ventana de compilación previamente abierta.
  • C-c h (en C): Cambia entre el buffer con el encabezado y las implementaciones.
  • C-c e (en C): Lista los métodos de un archivos.
  • C-c C-r (en C): Busca referencias a una etiqueta (función, variable, clase, tipo…) dentro de
    un buffer.
  • F1 (en PHP): Muestra ayuda del manual sobre una función del lenguaje. La busca en php.net y visualiza las primeras líneas.
  • F1 (en Python): Muestra ayuda del manual sobre una función o cualquier símbolo
    del lenguaje.
  • C-F1 (en PHP): Se dirige a la ayuda en Internet a través del navegador.
  • C-] (en PHP y Python): Mueve el cursor hacia la definición de la función. Cuando ésta está en el mismo buffer.
  • C-t (en PHP): Vuelve donde estábamos antes. Si por ejemplo, nos hemos movido hacia la definición de función.

Más modos y configuraciones

Seguro que existen muchísimos modos de Emacs más para programar. Me gustaría saber si utilizas alguno que te haya salvado la vida, o funcione realmente bien para los lenguajes con los que trabajas a diario.

Foto principal: unsplash-logoJoão Silas

También podría interesarte...

Leave a Reply