1 ;;; electric-spacing.el --- Insert operators with surrounding spaces smartly
3 ;; Copyright (C) 2004, 2005, 2007-2014 Free Software Foundation, Inc.
5 ;; Author: William Xu <william.xwl@gmail.com>
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 3, or (at your option)
13 ;; This program is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;; General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with EMMS; see the file COPYING. If not, write to the
20 ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 ;; Boston, MA 02110-1301, USA.
25 ;; Smart Operator mode is a minor mode which automatically inserts
26 ;; surrounding spaces around operator symbols. For example, `='
27 ;; becomes ` = ', `+=' becomes ` += '. This is most handy for writing
28 ;; C-style source code.
30 ;; Type `M-x smart-operator-mode' to toggle this minor mode.
34 ;; Nikolaj Schumacher <n_schumacher@web.de>, for suggesting
35 ;; reimplementing as a minor mode and providing an initial patch for
42 ;;; electric-spacing minor mode
44 (defcustom electric-spacing-double-space-docs t
45 "Enable double spacing of . in document lines - e.g., type `.' => get `. '."
49 (defcustom electric-spacing-docs t
50 "Enable electric-spacing in strings and comments."
54 (defvar electric-spacing-rules
55 '((?= . electric-spacing-self-insert-command)
56 (?< . electric-spacing-<)
57 (?> . electric-spacing->)
58 (?% . electric-spacing-%)
59 (?+ . electric-spacing-+)
60 (?- . electric-spacing--)
61 (?* . electric-spacing-*)
62 (?/ . electric-spacing-/)
63 (?& . electric-spacing-&)
64 (?| . electric-spacing-self-insert-command)
65 (?: . electric-spacing-:)
66 (?? . electric-spacing-?)
67 (?, . electric-spacing-\,)
68 (?~ . electric-spacing-~)
69 (?. . electric-spacing-.)))
71 (defun electric-spacing-post-self-insert-function ()
72 (when electric-spacing-mode
73 (let ((rule (cdr (assq last-command-event electric-spacing-rules))))
75 (goto-char (electric--after-char-pos))
79 (add-hook 'post-self-insert-hook #'electric-spacing-post-self-insert-function)
82 (define-minor-mode electric-spacing-mode
83 "Toggle automatic surrounding space insertion (Electric Spacing mode).
84 With a prefix argument ARG, enable Electric Spacing mode if ARG is
85 positive, and disable it otherwise. If called from Lisp, enable
86 the mode if ARG is omitted or nil.
88 This is a local minor mode. When enabled, typing an operator automatically
89 inserts surrounding spaces. e.g., `=' becomes ` = ', `+=' becomes ` += '.
90 This is very handy for many programming languages."
95 (defun electric-spacing-self-insert-command ()
96 "Insert character with surrounding spaces."
97 (electric-spacing-insert (string last-command-event)))
99 (defun electric-spacing-insert (op &optional only-where)
100 "See `electric-spacing-insert-1'."
101 (delete-horizontal-space)
102 (cond ((and (electric-spacing-lispy-mode?)
103 (not (electric-spacing-document?)))
104 (electric-spacing-lispy op))
105 ((not electric-spacing-docs)
106 (electric-spacing-insert-1 op 'middle))
108 (electric-spacing-insert-1 op only-where))))
110 (defun electric-spacing-insert-1 (op &optional only-where)
111 "Insert operator OP with surrounding spaces.
112 e.g., `=' becomes ` = ', `+=' becomes ` += '.
114 When `only-where' is 'after, we will insert space at back only;
115 when `only-where' is 'before, we will insert space at front only;
116 when `only-where' is 'middle, we will not insert space."
118 (`before (insert " " op))
119 (`middle (insert op))
120 (`after (insert op " "))
122 (let ((begin? (bolp)))
123 (unless (or (looking-back (regexp-opt
124 (mapcar 'char-to-string
125 (mapcar 'car electric-spacing-rules)))
126 (line-beginning-position))
131 (indent-according-to-mode))))))
133 (defun electric-spacing-c-types ()
134 (concat c-primitive-type-key "?"))
136 (defun electric-spacing-document? ()
137 (nth 8 (syntax-ppss)))
139 (defun electric-spacing-lispy-mode? ()
140 (derived-mode-p 'emacs-lisp-mode
142 'lisp-interaction-mode
145 (defun electric-spacing-lispy (op)
146 "We're in a Lisp-ish mode, so let's look for parenthesis.
147 Meanwhile, if not found after ( operators are more likely to be function names,
148 so let's not get too insert-happy."
154 (electric-spacing-insert-1 op 'middle)
155 (electric-spacing-insert-1 op 'after)))
157 (electric-spacing-insert-1 op 'before))
159 (electric-spacing-insert-1 op 'middle))))
164 (defun electric-spacing-< ()
165 "See `electric-spacing-insert'."
167 ((or (and c-buffer-is-cc-mode
171 '("#include" "vector" "deque" "list" "map" "stack"
172 "multimap" "set" "hash_map" "iterator" "template"
173 "pair" "auto_ptr" "static_cast"
174 "dynmaic_cast" "const_cast" "reintepret_cast"
178 (line-beginning-position)))
179 (derived-mode-p 'sgml-mode))
183 (electric-spacing-insert "<"))))
185 (defun electric-spacing-: ()
186 "See `electric-spacing-insert'."
187 (cond (c-buffer-is-cc-mode
188 (if (looking-back "\\?.+")
189 (electric-spacing-insert ":")
190 (electric-spacing-insert ":" 'middle)))
191 ((derived-mode-p 'haskell-mode)
192 (electric-spacing-insert ":"))
194 (electric-spacing-insert ":" 'after))))
196 (defun electric-spacing-\, ()
197 "See `electric-spacing-insert'."
198 (electric-spacing-insert "," 'after))
200 (defun electric-spacing-. ()
201 "See `electric-spacing-insert'."
202 (cond ((and electric-spacing-double-space-docs
203 (electric-spacing-document?))
204 (electric-spacing-insert "." 'after)
206 ((or (looking-back "[0-9]")
207 (or (and c-buffer-is-cc-mode
208 (looking-back "[a-z]"))
210 (derived-mode-p 'python-mode 'ruby-mode)
211 (looking-back "[a-z\)]"))
213 (derived-mode-p 'js-mode 'js2-mode)
214 (looking-back "[a-z\)$]"))))
216 ((derived-mode-p 'cperl-mode 'perl-mode 'ruby-mode)
217 ;; Check for the .. range operator
218 (if (looking-back ".")
222 (electric-spacing-insert "." 'after)
225 (defun electric-spacing-& ()
226 "See `electric-spacing-insert'."
227 (cond (c-buffer-is-cc-mode
229 ;; | char &a = b; // FIXME
230 ;; | void foo(const int& a);
235 (cond ((looking-back (concat (electric-spacing-c-types) " *" ))
236 (electric-spacing-insert "&" 'after))
237 ((looking-back "= *")
238 (electric-spacing-insert "&" 'before))
240 (electric-spacing-insert "&"))))
242 (electric-spacing-insert "&"))))
244 (defun electric-spacing-* ()
245 "See `electric-spacing-insert'."
246 (cond (c-buffer-is-cc-mode
255 (cond ((looking-back (concat (electric-spacing-c-types) " *" ))
256 (electric-spacing-insert "*" 'before))
257 ((looking-back "\\* *")
258 (electric-spacing-insert "*" 'middle))
259 ((looking-back "^[ (]*")
260 (electric-spacing-insert "*" 'middle)
261 (indent-according-to-mode))
262 ((looking-back "= *")
263 (electric-spacing-insert "*" 'before))
265 (electric-spacing-insert "*"))))
267 (electric-spacing-insert "*"))))
269 (defun electric-spacing-> ()
270 "See `electric-spacing-insert'."
271 (cond ((and c-buffer-is-cc-mode (looking-back " - "))
275 (electric-spacing-insert ">"))))
277 (defun electric-spacing-+ ()
278 "See `electric-spacing-insert'."
279 (cond ((and c-buffer-is-cc-mode (looking-back "\\+ *"))
280 (when (looking-back "[a-zA-Z0-9_] +\\+ *")
283 (delete-horizontal-space)))
284 (electric-spacing-insert "+" 'middle)
285 (indent-according-to-mode))
287 (electric-spacing-insert "+"))))
289 (defun electric-spacing-- ()
290 "See `electric-spacing-insert'."
291 (cond ((and c-buffer-is-cc-mode (looking-back "\\- *"))
292 (when (looking-back "[a-zA-Z0-9_] +\\- *")
295 (delete-horizontal-space)))
296 (electric-spacing-insert "-" 'middle)
297 (indent-according-to-mode))
299 (electric-spacing-insert "-"))))
301 (defun electric-spacing-? ()
302 "See `electric-spacing-insert'."
303 (cond (c-buffer-is-cc-mode
304 (electric-spacing-insert "?"))
306 (electric-spacing-insert "?" 'after))))
308 (defun electric-spacing-% ()
309 "See `electric-spacing-insert'."
310 (cond (c-buffer-is-cc-mode
313 ;; | printf("%d %d\n", a % b);
315 (if (and (looking-back "\".*")
316 (not (looking-back "\",.*")))
318 (electric-spacing-insert "%")))
319 ;; If this is a comment or string, we most likely
320 ;; want no spaces - probably string formatting
321 ((and (derived-mode-p 'python-mode)
322 (electric-spacing-document?))
325 (electric-spacing-insert "%"))))
327 (defun electric-spacing-~ ()
328 "See `electric-spacing-insert'."
329 ;; First class regex operator =~ langs
330 (cond ((derived-mode-p 'ruby-mode 'perl-mode 'cperl-mode)
331 (if (looking-back "= ")
339 (defun electric-spacing-/ ()
340 "See `electric-spacing-insert'."
342 (cond ((and (eq 1 (line-number-at-pos))
344 (move-beginning-of-line nil)
348 (electric-spacing-insert "/"))))
350 (provide 'electric-spacing)
352 ;;; electric-spacing.el ends here