my-emacs-d/elpa/electric-spacing-20151209.736/electric-spacing.el

406 lines
13 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; electric-spacing.el --- Insert operators with surrounding spaces smartly
;; Copyright (C) 2004, 2005, 2007-2015 Free Software Foundation, Inc.
;; Author: William Xu <william.xwl@gmail.com>
;; Version: 5.0
;; Package-Version: 20151209.736
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with EMMS; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Smart Operator mode is a minor mode which automatically inserts
;; surrounding spaces around operator symbols. For example, `='
;; becomes ` = ', `+=' becomes ` += '. This is most handy for writing
;; C-style source code.
;;
;; Type `M-x electric-spacing-mode' to toggle this minor mode.
;;; Acknowledgements
;; Nikolaj Schumacher <n_schumacher@web.de>, for suggesting
;; reimplementing as a minor mode and providing an initial patch for
;; that.
;;; Code:
(require 'cc-mode)
(require 'thingatpt)
;;; electric-spacing minor mode
(defcustom electric-spacing-double-space-docs t
"Enable double spacing of . in document lines - e,g, type '.' => get '. '."
:type 'boolean
:group 'electricity)
(defcustom electric-spacing-docs t
"Enable electric-spacing in strings and comments."
:type 'boolean
:group 'electricity)
(defvar electric-spacing-rules
'((?= . electric-spacing-self-insert-command)
(?< . electric-spacing-<)
(?> . electric-spacing->)
(?% . electric-spacing-%)
(?+ . electric-spacing-+)
(?- . electric-spacing--)
(?* . electric-spacing-*)
(?/ . electric-spacing-/)
(?& . electric-spacing-&)
(?| . electric-spacing-self-insert-command)
(?: . electric-spacing-:)
(?? . electric-spacing-?)
(?, . electric-spacing-\,)
(?~ . electric-spacing-~)
(?. . electric-spacing-.)
(?^ . electric-spacing-self-insert-command)))
(defun electric-spacing-post-self-insert-function ()
(when (electric-spacing-should-run?)
(let ((rule (cdr (assq last-command-event electric-spacing-rules))))
(when rule
(goto-char (electric--after-char-pos))
(delete-char -1)
(funcall rule)))))
;;;###autoload
(define-minor-mode electric-spacing-mode
"Toggle automatic surrounding space insertion (Electric Spacing mode).
With a prefix argument ARG, enable Electric Spacing mode if ARG is
positive, and disable it otherwise. If called from Lisp, enable
the mode if ARG is omitted or nil.
This is a local minor mode. When enabled, typing an operator automatically
inserts surrounding spaces. e.g., `=' becomes ` = ',`+=' becomes ` += '. This
is very handy for many programming languages."
:global nil
:group 'electricity
:lighter " _+_"
;; body
(if electric-spacing-mode
(add-hook 'post-self-insert-hook
#'electric-spacing-post-self-insert-function nil t)
(remove-hook 'post-self-insert-hook
#'electric-spacing-post-self-insert-function t)))
(defun electric-spacing-self-insert-command ()
"Insert character with surrounding spaces."
(electric-spacing-insert (string last-command-event)))
(defun electric-spacing-insert (op &optional only-where)
"See `electric-spacing-insert-1'."
(delete-horizontal-space)
(cond ((and (electric-spacing-lispy-mode?)
(not (electric-spacing-document?)))
(electric-spacing-lispy op))
(t
(electric-spacing-insert-1 op only-where))))
(defun electric-spacing-insert-1 (op &optional only-where)
"Insert operator OP with surrounding spaces.
e.g., `=' becomes ` = ', `+=' becomes ` += '.
When `only-where' is 'after, we will insert space at back only;
when `only-where' is 'before, we will insert space at front only;
when `only-where' is 'middle, we will not insert space."
(pcase only-where
(`before (insert " " op))
(`middle (insert op))
(`after (insert op " "))
(_
(let ((begin? (bolp)))
(unless (or (looking-back (regexp-opt
(mapcar 'char-to-string
(mapcar 'car electric-spacing-rules)))
(line-beginning-position))
begin?)
(insert " "))
(insert op " ")
(when begin?
(indent-according-to-mode))))))
(defun electric-spacing-c-types ()
(concat c-primitive-type-key "?"))
(defun electric-spacing-document? ()
(nth 8 (syntax-ppss)))
(defun electric-spacing-should-run? ()
(or (not electric-spacing-docs)
(not (electric-spacing-document?))))
(defun electric-spacing-lispy-mode? ()
(derived-mode-p 'emacs-lisp-mode
'lisp-mode
'lisp-interaction-mode
'scheme-mode))
(defun electric-spacing-lispy (op)
"We're in a Lisp-ish mode, so let's look for parenthesis.
Meanwhile, if not found after ( operators are more likely to be function names,
so let's not get too insert-happy."
(cond
((save-excursion
(backward-char 1)
(looking-at "("))
(if (equal op ",")
(electric-spacing-insert-1 op 'middle)
(electric-spacing-insert-1 op 'after)))
((equal op ",")
(electric-spacing-insert-1 op 'before))
(t
(electric-spacing-insert-1 op 'middle))))
(defconst electric-spacing-operators-regexp
(regexp-opt
(mapcar (lambda (el) (char-to-string (car el)))
electric-spacing-rules)))
;;; Fine Tunings
(defun electric-spacing-< ()
"See `electric-spacing-insert'."
(cond
((or (and c-buffer-is-cc-mode
(looking-back
(concat "\\("
(regexp-opt
'("#include" "vector" "deque" "list" "map" "stack"
"multimap" "set" "hash_map" "iterator" "template"
"pair" "auto_ptr" "static_cast"
"dynmaic_cast" "const_cast" "reintepret_cast"
"#import"))
"\\)\\ *")
(line-beginning-position)))
(derived-mode-p 'sgml-mode))
(insert "<>")
(backward-char))
(t
(electric-spacing-insert "<"))))
(defun electric-spacing-: ()
"See `electric-spacing-insert'."
(cond (c-buffer-is-cc-mode
(if (looking-back "\\?.+")
(electric-spacing-insert ":")
(electric-spacing-insert ":" 'middle)))
((derived-mode-p 'haskell-mode)
(electric-spacing-insert ":"))
((derived-mode-p 'python-mode) (electric-spacing-python-:))
((derived-mode-p 'ess-mode)
(insert ":"))
(t
(electric-spacing-insert ":" 'after))))
(defun electric-spacing-\, ()
"See `electric-spacing-insert'."
(electric-spacing-insert "," 'after))
(defun electric-spacing-. ()
"See `electric-spacing-insert'."
(cond ((and electric-spacing-double-space-docs
(electric-spacing-document?))
(electric-spacing-insert "." 'after)
(insert " "))
((or (looking-back "[0-9]")
(or (and c-buffer-is-cc-mode
(looking-back "[a-z]"))
(and
(derived-mode-p 'python-mode 'ruby-mode)
(looking-back "[a-z\)]"))
(and
(derived-mode-p 'js-mode 'js2-mode)
(looking-back "[a-z\)$]"))))
(insert "."))
((derived-mode-p 'cperl-mode 'perl-mode 'ruby-mode)
;; Check for the .. range operator
(if (looking-back ".")
(insert ".")
(insert " . ")))
(t
(electric-spacing-insert "." 'after)
(insert " "))))
(defun electric-spacing-& ()
"See `electric-spacing-insert'."
(cond (c-buffer-is-cc-mode
;; ,----[ cases ]
;; | char &a = b; // FIXME
;; | void foo(const int& a);
;; | char *a = &b;
;; | int c = a & b;
;; | a && b;
;; `----
(cond ((looking-back (concat (electric-spacing-c-types) " *" ))
(electric-spacing-insert "&" 'after))
((looking-back "= *")
(electric-spacing-insert "&" 'before))
(t
(electric-spacing-insert "&"))))
(t
(electric-spacing-insert "&"))))
(defun electric-spacing-* ()
"See `electric-spacing-insert'."
(cond (c-buffer-is-cc-mode
;; ,----
;; | a * b;
;; | char *a;
;; | char **b;
;; | (*a)->func();
;; | *p++;
;; | *a = *b;
;; `----
(cond ((looking-back (concat (electric-spacing-c-types) " *" ))
(electric-spacing-insert "*" 'before))
((looking-back "\\* *")
(electric-spacing-insert "*" 'middle))
((looking-back "^[ (]*")
(electric-spacing-insert "*" 'middle)
(indent-according-to-mode))
((looking-back "= *")
(electric-spacing-insert "*" 'before))
(t
(electric-spacing-insert "*"))))
;; Handle python *args and **kwargs
((derived-mode-p 'python-mode)
;; Can only occur after '(' ',' or on a new line, so just check
;; for those. If it's just after a comma then also insert a space
;; before the *.
(cond ((looking-back ",") (insert " *"))
((looking-back "[(,^)][ \t]*[*]?") (insert "*"))
;; Othewise act as normal
(t (electric-spacing-insert "*"))))
(t
(electric-spacing-insert "*"))))
(defun electric-spacing-> ()
"See `electric-spacing-insert'."
(cond ((and c-buffer-is-cc-mode (looking-back " - "))
(delete-char -3)
(insert "->"))
(t
(electric-spacing-insert ">"))))
(defun electric-spacing-+ ()
"See `electric-spacing-insert'."
(cond ((and c-buffer-is-cc-mode (looking-back "\\+ *"))
(when (looking-back "[a-zA-Z0-9_] +\\+ *")
(save-excursion
(backward-char 2)
(delete-horizontal-space)))
(electric-spacing-insert "+" 'middle)
(indent-according-to-mode))
(t
(electric-spacing-insert "+"))))
(defun electric-spacing-- ()
"See `electric-spacing-insert'."
(cond ((and c-buffer-is-cc-mode (looking-back "\\- *"))
(when (looking-back "[a-zA-Z0-9_] +\\- *")
(save-excursion
(backward-char 2)
(delete-horizontal-space)))
(electric-spacing-insert "-" 'middle)
(indent-according-to-mode))
;; exponent notation, e.g. 1e-10: don't space
((looking-back "[0-9.]+[eE]")
(insert "-"))
;; a = -9
((and (looking-back (concat electric-spacing-operators-regexp " *"))
(not (looking-back "- *")))
(electric-spacing-insert "-" 'before))
(t
(electric-spacing-insert "-"))))
(defun electric-spacing-? ()
"See `electric-spacing-insert'."
(cond (c-buffer-is-cc-mode
(electric-spacing-insert "?"))
(t
(electric-spacing-insert "?" 'after))))
(defun electric-spacing-% ()
"See `electric-spacing-insert'."
(cond (c-buffer-is-cc-mode
;; ,----
;; | a % b;
;; | printf("%d %d\n", a % b);
;; `----
(if (and (looking-back "\".*")
(not (looking-back "\",.*")))
(insert "%")
(electric-spacing-insert "%")))
;; If this is a comment or string, we most likely
;; want no spaces - probably string formatting
((and (derived-mode-p 'python-mode)
(electric-spacing-document?))
(insert "%"))
(t
(electric-spacing-insert "%"))))
(defun electric-spacing-~ ()
"See `electric-spacing-insert'."
;; First class regex operator =~ langs
(cond ((derived-mode-p 'ruby-mode 'perl-mode 'cperl-mode)
(if (looking-back "= ")
(progn
(delete-char -2)
(insert "=~ "))
(insert "~")))
(t
(insert "~"))))
(defun electric-spacing-/ ()
"See `electric-spacing-insert'."
;; *nix shebangs #!
(cond ((and (eq 1 (line-number-at-pos))
(save-excursion
(move-beginning-of-line nil)
(looking-at "#!")))
(insert "/"))
(t
(electric-spacing-insert "/"))))
(defun electric-spacing-enclosing-paren ()
"Return the opening parenthesis of the enclosing parens, or nil if not inside any parens."
(interactive)
(let ((ppss (syntax-ppss)))
(when (nth 1 ppss)
(char-after (nth 1 ppss)))))
(defun electric-spacing-python-: ()
(if (and (not (in-string-p))
(eq (electric-spacing-enclosing-paren) ?\{))
(electric-spacing-insert ":" 'after)
(insert ":")))
(provide 'electric-spacing)
;;; electric-spacing.el ends here