| 1 | ;;; elec-pair.el --- Automatic parenthesis pairing -*- lexical-binding:t -*- |
| 2 | |
| 3 | ;; Copyright (C) 2013-2014 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: João Távora <joaotavora@gmail.com> |
| 6 | |
| 7 | ;; This file is part of GNU Emacs. |
| 8 | |
| 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
| 10 | ;; it under the terms of the GNU General Public License as published by |
| 11 | ;; the Free Software Foundation, either version 3 of the License, or |
| 12 | ;; (at your option) any later version. |
| 13 | |
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | ;; GNU General Public License for more details. |
| 18 | |
| 19 | ;; You should have received a copy of the GNU General Public License |
| 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
| 21 | |
| 22 | ;;; Commentary: |
| 23 | |
| 24 | ;;; Code: |
| 25 | |
| 26 | (require 'electric) |
| 27 | |
| 28 | ;;; Electric pairing. |
| 29 | |
| 30 | (defcustom electric-pair-pairs |
| 31 | '((?\" . ?\")) |
| 32 | "Alist of pairs that should be used regardless of major mode. |
| 33 | |
| 34 | Pairs of delimiters in this list are a fallback in case they have |
| 35 | no syntax relevant to `electric-pair-mode' in the mode's syntax |
| 36 | table. |
| 37 | |
| 38 | See also the variable `electric-pair-text-pairs'." |
| 39 | :version "24.1" |
| 40 | :group 'electricity |
| 41 | :type '(repeat (cons character character))) |
| 42 | |
| 43 | ;;;###autoload |
| 44 | (defcustom electric-pair-text-pairs |
| 45 | '((?\" . ?\" )) |
| 46 | "Alist of pairs that should always be used in comments and strings. |
| 47 | |
| 48 | Pairs of delimiters in this list are a fallback in case they have |
| 49 | no syntax relevant to `electric-pair-mode' in the syntax table |
| 50 | defined in `electric-pair-text-syntax-table'" |
| 51 | :version "24.4" |
| 52 | :group 'electricity |
| 53 | :type '(repeat (cons character character))) |
| 54 | |
| 55 | (defcustom electric-pair-skip-self #'electric-pair-default-skip-self |
| 56 | "If non-nil, skip char instead of inserting a second closing paren. |
| 57 | |
| 58 | When inserting a closing paren character right before the same character, |
| 59 | just skip that character instead, so that hitting ( followed by ) results |
| 60 | in \"()\" rather than \"())\". |
| 61 | |
| 62 | This can be convenient for people who find it easier to hit ) than C-f. |
| 63 | |
| 64 | Can also be a function of one argument (the closer char just |
| 65 | inserted), in which case that function's return value is |
| 66 | considered instead." |
| 67 | :version "24.1" |
| 68 | :group 'electricity |
| 69 | :type '(choice |
| 70 | (const :tag "Never skip" nil) |
| 71 | (const :tag "Help balance" electric-pair-default-skip-self) |
| 72 | (const :tag "Always skip" t) |
| 73 | function)) |
| 74 | |
| 75 | (defcustom electric-pair-inhibit-predicate |
| 76 | #'electric-pair-default-inhibit |
| 77 | "Predicate to prevent insertion of a matching pair. |
| 78 | |
| 79 | The function is called with a single char (the opening char just inserted). |
| 80 | If it returns non-nil, then `electric-pair-mode' will not insert a matching |
| 81 | closer." |
| 82 | :version "24.4" |
| 83 | :group 'electricity |
| 84 | :type '(choice |
| 85 | (const :tag "Conservative" electric-pair-conservative-inhibit) |
| 86 | (const :tag "Help balance" electric-pair-default-inhibit) |
| 87 | (const :tag "Always pair" ignore) |
| 88 | function)) |
| 89 | |
| 90 | (defcustom electric-pair-preserve-balance t |
| 91 | "Non-nil if default pairing and skipping should help balance parentheses. |
| 92 | |
| 93 | The default values of `electric-pair-inhibit-predicate' and |
| 94 | `electric-pair-skip-self' check this variable before delegating to other |
| 95 | predicates responsible for making decisions on whether to pair/skip some |
| 96 | characters based on the actual state of the buffer's parentheses and |
| 97 | quotes." |
| 98 | :version "24.4" |
| 99 | :group 'electricity |
| 100 | :type 'boolean) |
| 101 | |
| 102 | (defcustom electric-pair-delete-adjacent-pairs t |
| 103 | "If non-nil, backspacing an open paren also deletes adjacent closer. |
| 104 | |
| 105 | Can also be a function of no arguments, in which case that function's |
| 106 | return value is considered instead." |
| 107 | :version "24.4" |
| 108 | :group 'electricity |
| 109 | :type '(choice |
| 110 | (const :tag "Yes" t) |
| 111 | (const :tag "No" nil) |
| 112 | function)) |
| 113 | |
| 114 | (defcustom electric-pair-open-newline-between-pairs t |
| 115 | "If non-nil, a newline between adjacent parentheses opens an extra one. |
| 116 | |
| 117 | Can also be a function of no arguments, in which case that function's |
| 118 | return value is considered instead." |
| 119 | :version "24.4" |
| 120 | :group 'electricity |
| 121 | :type '(choice |
| 122 | (const :tag "Yes" t) |
| 123 | (const :tag "No" nil) |
| 124 | function)) |
| 125 | |
| 126 | (defcustom electric-pair-skip-whitespace t |
| 127 | "If non-nil skip whitespace when skipping over closing parens. |
| 128 | |
| 129 | The specific kind of whitespace skipped is given by the variable |
| 130 | `electric-pair-skip-whitespace-chars'. |
| 131 | |
| 132 | The symbol `chomp' specifies that the skipped-over whitespace |
| 133 | should be deleted. |
| 134 | |
| 135 | Can also be a function of no arguments, in which case that function's |
| 136 | return value is considered instead." |
| 137 | :version "24.4" |
| 138 | :group 'electricity |
| 139 | :type '(choice |
| 140 | (const :tag "Yes, jump over whitespace" t) |
| 141 | (const :tag "Yes, and delete whitespace" chomp) |
| 142 | (const :tag "No, no whitespace skipping" nil) |
| 143 | function)) |
| 144 | |
| 145 | (defcustom electric-pair-skip-whitespace-chars (list ?\t ?\s ?\n) |
| 146 | "Whitespace characters considered by `electric-pair-skip-whitespace'." |
| 147 | :version "24.4" |
| 148 | :group 'electricity |
| 149 | :type '(choice (set (const :tag "Space" ?\s) |
| 150 | (const :tag "Tab" ?\t) |
| 151 | (const :tag "Newline" ?\n)) |
| 152 | (list character))) |
| 153 | |
| 154 | (defun electric-pair--skip-whitespace () |
| 155 | "Skip whitespace forward, not crossing comment or string boundaries." |
| 156 | (let ((saved (point)) |
| 157 | (string-or-comment (nth 8 (syntax-ppss)))) |
| 158 | (skip-chars-forward (apply #'string electric-pair-skip-whitespace-chars)) |
| 159 | (unless (eq string-or-comment (nth 8 (syntax-ppss))) |
| 160 | (goto-char saved)))) |
| 161 | |
| 162 | (defvar electric-pair-text-syntax-table prog-mode-syntax-table |
| 163 | "Syntax table used when pairing inside comments and strings. |
| 164 | |
| 165 | `electric-pair-mode' considers this syntax table only when point in inside |
| 166 | quotes or comments. If lookup fails here, `electric-pair-text-pairs' will |
| 167 | be considered.") |
| 168 | |
| 169 | (defun electric-pair-conservative-inhibit (char) |
| 170 | (or |
| 171 | ;; I find it more often preferable not to pair when the |
| 172 | ;; same char is next. |
| 173 | (eq char (char-after)) |
| 174 | ;; Don't pair up when we insert the second of "" or of ((. |
| 175 | (and (eq char (char-before)) |
| 176 | (eq char (char-before (1- (point))))) |
| 177 | ;; I also find it often preferable not to pair next to a word. |
| 178 | (eq (char-syntax (following-char)) ?w))) |
| 179 | |
| 180 | (defun electric-pair-syntax-info (command-event) |
| 181 | "Calculate a list (SYNTAX PAIR UNCONDITIONAL STRING-OR-COMMENT-START). |
| 182 | |
| 183 | SYNTAX is COMMAND-EVENT's syntax character. PAIR is |
| 184 | COMMAND-EVENT's pair. UNCONDITIONAL indicates the variables |
| 185 | `electric-pair-pairs' or `electric-pair-text-pairs' were used to |
| 186 | lookup syntax. STRING-OR-COMMENT-START indicates that point is |
| 187 | inside a comment or string." |
| 188 | (let* ((pre-string-or-comment (or (bobp) |
| 189 | (nth 8 (save-excursion |
| 190 | (syntax-ppss (1- (point))))))) |
| 191 | (post-string-or-comment (nth 8 (syntax-ppss (point)))) |
| 192 | (string-or-comment (and post-string-or-comment |
| 193 | pre-string-or-comment)) |
| 194 | (table (if string-or-comment |
| 195 | electric-pair-text-syntax-table |
| 196 | (syntax-table))) |
| 197 | (table-syntax-and-pair (with-syntax-table table |
| 198 | (list (char-syntax command-event) |
| 199 | (or (matching-paren command-event) |
| 200 | command-event)))) |
| 201 | (fallback (if string-or-comment |
| 202 | (append electric-pair-text-pairs |
| 203 | electric-pair-pairs) |
| 204 | electric-pair-pairs)) |
| 205 | (direct (assq command-event fallback)) |
| 206 | (reverse (rassq command-event fallback))) |
| 207 | (cond |
| 208 | ((memq (car table-syntax-and-pair) |
| 209 | '(?\" ?\( ?\) ?\$)) |
| 210 | (append table-syntax-and-pair (list nil string-or-comment))) |
| 211 | (direct (if (eq (car direct) (cdr direct)) |
| 212 | (list ?\" command-event t string-or-comment) |
| 213 | (list ?\( (cdr direct) t string-or-comment))) |
| 214 | (reverse (list ?\) (car reverse) t string-or-comment))))) |
| 215 | |
| 216 | (defun electric-pair--insert (char) |
| 217 | (let ((last-command-event char) |
| 218 | (blink-matching-paren nil) |
| 219 | (electric-pair-mode nil)) |
| 220 | (self-insert-command 1))) |
| 221 | |
| 222 | (defun electric-pair--syntax-ppss (&optional pos where) |
| 223 | "Like `syntax-ppss', but sometimes fallback to `parse-partial-sexp'. |
| 224 | |
| 225 | WHERE is a list defaulting to '(string comment) and indicates |
| 226 | when to fallback to `parse-partial-sexp'." |
| 227 | (let* ((pos (or pos (point))) |
| 228 | (where (or where '(string comment))) |
| 229 | (quick-ppss (syntax-ppss pos)) |
| 230 | (in-string (and (nth 3 quick-ppss) (memq 'string where))) |
| 231 | (in-comment (and (nth 4 quick-ppss) (memq 'comment where))) |
| 232 | (s-or-c-start (cond (in-string |
| 233 | (1+ (nth 8 quick-ppss))) |
| 234 | (in-comment |
| 235 | (goto-char (nth 8 quick-ppss)) |
| 236 | (forward-comment (- (point-max))) |
| 237 | (skip-syntax-forward " >!") |
| 238 | (point))))) |
| 239 | (if s-or-c-start |
| 240 | (with-syntax-table electric-pair-text-syntax-table |
| 241 | (parse-partial-sexp s-or-c-start pos)) |
| 242 | ;; HACK! cc-mode apparently has some `syntax-ppss' bugs |
| 243 | (if (memq major-mode '(c-mode c++ mode)) |
| 244 | (parse-partial-sexp (point-min) pos) |
| 245 | quick-ppss)))) |
| 246 | |
| 247 | ;; Balancing means controlling pairing and skipping of parentheses |
| 248 | ;; so that, if possible, the buffer ends up at least as balanced as |
| 249 | ;; before, if not more. The algorithm is slightly complex because |
| 250 | ;; some situations like "()))" need pairing to occur at the end but |
| 251 | ;; not at the beginning. Balancing should also happen independently |
| 252 | ;; for different types of parentheses, so that having your {}'s |
| 253 | ;; unbalanced doesn't keep `electric-pair-mode' from balancing your |
| 254 | ;; ()'s and your []'s. |
| 255 | (defun electric-pair--balance-info (direction string-or-comment) |
| 256 | "Examine lists forward or backward according to DIRECTION's sign. |
| 257 | |
| 258 | STRING-OR-COMMENT is info suitable for running `parse-partial-sexp'. |
| 259 | |
| 260 | Return a cons of two descriptions (MATCHED-P . PAIR) for the |
| 261 | innermost and outermost lists that enclose point. The outermost |
| 262 | list enclosing point is either the first top-level or first |
| 263 | mismatched list found by listing up. |
| 264 | |
| 265 | If the outermost list is matched, don't rely on its PAIR. |
| 266 | If point is not enclosed by any lists, return ((t) . (t))." |
| 267 | (let* (innermost |
| 268 | outermost |
| 269 | (table (if string-or-comment |
| 270 | electric-pair-text-syntax-table |
| 271 | (syntax-table))) |
| 272 | (at-top-level-or-equivalent-fn |
| 273 | ;; called when `scan-sexps' ran perfectly, when it found |
| 274 | ;; a parenthesis pointing in the direction of travel. |
| 275 | ;; Also when travel started inside a comment and exited it. |
| 276 | #'(lambda () |
| 277 | (setq outermost (list t)) |
| 278 | (unless innermost |
| 279 | (setq innermost (list t))))) |
| 280 | (ended-prematurely-fn |
| 281 | ;; called when `scan-sexps' crashed against a parenthesis |
| 282 | ;; pointing opposite the direction of travel. After |
| 283 | ;; traversing that character, the idea is to travel one sexp |
| 284 | ;; in the opposite direction looking for a matching |
| 285 | ;; delimiter. |
| 286 | #'(lambda () |
| 287 | (let* ((pos (point)) |
| 288 | (matched |
| 289 | (save-excursion |
| 290 | (cond ((< direction 0) |
| 291 | (condition-case nil |
| 292 | (eq (char-after pos) |
| 293 | (with-syntax-table table |
| 294 | (matching-paren |
| 295 | (char-before |
| 296 | (scan-sexps (point) 1))))) |
| 297 | (scan-error nil))) |
| 298 | (t |
| 299 | ;; In this case, no need to use |
| 300 | ;; `scan-sexps', we can use some |
| 301 | ;; `electric-pair--syntax-ppss' in this |
| 302 | ;; case (which uses the quicker |
| 303 | ;; `syntax-ppss' in some cases) |
| 304 | (let* ((ppss (electric-pair--syntax-ppss |
| 305 | (1- (point)))) |
| 306 | (start (car (last (nth 9 ppss)))) |
| 307 | (opener (char-after start))) |
| 308 | (and start |
| 309 | (eq (char-before pos) |
| 310 | (or (with-syntax-table table |
| 311 | (matching-paren opener)) |
| 312 | opener)))))))) |
| 313 | (actual-pair (if (> direction 0) |
| 314 | (char-before (point)) |
| 315 | (char-after (point))))) |
| 316 | (unless innermost |
| 317 | (setq innermost (cons matched actual-pair))) |
| 318 | (unless matched |
| 319 | (setq outermost (cons matched actual-pair))))))) |
| 320 | (save-excursion |
| 321 | (while (not outermost) |
| 322 | (condition-case err |
| 323 | (with-syntax-table table |
| 324 | (scan-sexps (point) (if (> direction 0) |
| 325 | (point-max) |
| 326 | (- (point-max)))) |
| 327 | (funcall at-top-level-or-equivalent-fn)) |
| 328 | (scan-error |
| 329 | (cond ((or |
| 330 | ;; some error happened and it is not of the "ended |
| 331 | ;; prematurely" kind... |
| 332 | (not (string-match "ends prematurely" (nth 1 err))) |
| 333 | ;; ... or we were in a comment and just came out of |
| 334 | ;; it. |
| 335 | (and string-or-comment |
| 336 | (not (nth 8 (syntax-ppss))))) |
| 337 | (funcall at-top-level-or-equivalent-fn)) |
| 338 | (t |
| 339 | ;; exit the sexp |
| 340 | (goto-char (nth 3 err)) |
| 341 | (funcall ended-prematurely-fn))))))) |
| 342 | (cons innermost outermost))) |
| 343 | |
| 344 | (defvar electric-pair-string-bound-function 'point-max |
| 345 | "Next buffer position where strings are syntactically unexpected. |
| 346 | Value is a function called with no arguments and returning a |
| 347 | buffer position. Major modes should set this variable |
| 348 | buffer-locally if they experience slowness with |
| 349 | `electric-pair-mode' when pairing quotes.") |
| 350 | |
| 351 | (defun electric-pair--unbalanced-strings-p (char) |
| 352 | "Return non-nil if there are unbalanced strings started by CHAR." |
| 353 | (let* ((selector-ppss (syntax-ppss)) |
| 354 | (relevant-ppss (save-excursion |
| 355 | (if (nth 4 selector-ppss) ; comment |
| 356 | (electric-pair--syntax-ppss |
| 357 | (progn |
| 358 | (goto-char (nth 8 selector-ppss)) |
| 359 | (forward-comment (point-max)) |
| 360 | (skip-syntax-backward " >!") |
| 361 | (point))) |
| 362 | (syntax-ppss |
| 363 | (funcall electric-pair-string-bound-function))))) |
| 364 | (string-delim (nth 3 relevant-ppss))) |
| 365 | (or (eq t string-delim) |
| 366 | (eq char string-delim)))) |
| 367 | |
| 368 | (defun electric-pair--inside-string-p (char) |
| 369 | "Return non-nil if point is inside a string started by CHAR. |
| 370 | |
| 371 | A comments text is parsed with `electric-pair-text-syntax-table'. |
| 372 | Also consider strings within comments, but not strings within |
| 373 | strings." |
| 374 | ;; FIXME: could also consider strings within strings by examining |
| 375 | ;; delimiters. |
| 376 | (let ((ppss (electric-pair--syntax-ppss (point) '(comment)))) |
| 377 | (memq (nth 3 ppss) (list t char)))) |
| 378 | |
| 379 | (defun electric-pair-inhibit-if-helps-balance (char) |
| 380 | "Return non-nil if auto-pairing of CHAR would hurt parentheses' balance. |
| 381 | |
| 382 | Works by first removing the character from the buffer, then doing |
| 383 | some list calculations, finally restoring the situation as if nothing |
| 384 | happened." |
| 385 | (pcase (electric-pair-syntax-info char) |
| 386 | (`(,syntax ,pair ,_ ,s-or-c) |
| 387 | (unwind-protect |
| 388 | (progn |
| 389 | (delete-char -1) |
| 390 | (cond ((eq ?\( syntax) |
| 391 | (let* ((pair-data |
| 392 | (electric-pair--balance-info 1 s-or-c)) |
| 393 | (outermost (cdr pair-data))) |
| 394 | (cond ((car outermost) |
| 395 | nil) |
| 396 | (t |
| 397 | (eq (cdr outermost) pair))))) |
| 398 | ((eq syntax ?\") |
| 399 | (electric-pair--unbalanced-strings-p char)))) |
| 400 | (insert-char char))))) |
| 401 | |
| 402 | (defun electric-pair-skip-if-helps-balance (char) |
| 403 | "Return non-nil if skipping CHAR would benefit parentheses' balance. |
| 404 | |
| 405 | Works by first removing the character from the buffer, then doing |
| 406 | some list calculations, finally restoring the situation as if nothing |
| 407 | happened." |
| 408 | (pcase (electric-pair-syntax-info char) |
| 409 | (`(,syntax ,pair ,_ ,s-or-c) |
| 410 | (unwind-protect |
| 411 | (progn |
| 412 | (delete-char -1) |
| 413 | (cond ((eq syntax ?\)) |
| 414 | (let* ((pair-data |
| 415 | (electric-pair--balance-info |
| 416 | -1 s-or-c)) |
| 417 | (innermost (car pair-data)) |
| 418 | (outermost (cdr pair-data))) |
| 419 | (and |
| 420 | (cond ((car outermost) |
| 421 | (car innermost)) |
| 422 | ((car innermost) |
| 423 | (not (eq (cdr outermost) pair))))))) |
| 424 | ((eq syntax ?\") |
| 425 | (electric-pair--inside-string-p char)))) |
| 426 | (insert-char char))))) |
| 427 | |
| 428 | (defun electric-pair-default-skip-self (char) |
| 429 | (if electric-pair-preserve-balance |
| 430 | (electric-pair-skip-if-helps-balance char) |
| 431 | t)) |
| 432 | |
| 433 | (defun electric-pair-default-inhibit (char) |
| 434 | (if electric-pair-preserve-balance |
| 435 | (electric-pair-inhibit-if-helps-balance char) |
| 436 | (electric-pair-conservative-inhibit char))) |
| 437 | |
| 438 | (defun electric-pair-post-self-insert-function () |
| 439 | (let* ((pos (and electric-pair-mode (electric--after-char-pos))) |
| 440 | (skip-whitespace-info)) |
| 441 | (pcase (electric-pair-syntax-info last-command-event) |
| 442 | (`(,syntax ,pair ,unconditional ,_) |
| 443 | (cond |
| 444 | ((null pos) nil) |
| 445 | ;; Wrap a pair around the active region. |
| 446 | ;; |
| 447 | ((and (memq syntax '(?\( ?\) ?\" ?\$)) (use-region-p)) |
| 448 | ;; FIXME: To do this right, we'd need a post-self-insert-function |
| 449 | ;; so we could add-function around it and insert the closer after |
| 450 | ;; all the rest of the hook has run. |
| 451 | (if (or (eq syntax ?\") |
| 452 | (and (eq syntax ?\)) |
| 453 | (>= (point) (mark))) |
| 454 | (and (not (eq syntax ?\))) |
| 455 | (>= (mark) (point)))) |
| 456 | (save-excursion |
| 457 | (goto-char (mark)) |
| 458 | (electric-pair--insert pair)) |
| 459 | (delete-region pos (1- pos)) |
| 460 | (electric-pair--insert pair) |
| 461 | (goto-char (mark)) |
| 462 | (electric-pair--insert last-command-event))) |
| 463 | ;; Backslash-escaped: no pairing, no skipping. |
| 464 | ((save-excursion |
| 465 | (goto-char (1- pos)) |
| 466 | (not (zerop (% (skip-syntax-backward "\\") 2)))) |
| 467 | nil) |
| 468 | ;; Skip self. |
| 469 | ((and (memq syntax '(?\) ?\" ?\$)) |
| 470 | (and (or unconditional |
| 471 | (if (functionp electric-pair-skip-self) |
| 472 | (funcall electric-pair-skip-self last-command-event) |
| 473 | electric-pair-skip-self)) |
| 474 | (save-excursion |
| 475 | (when (and (not (and unconditional |
| 476 | (eq syntax ?\"))) |
| 477 | (setq skip-whitespace-info |
| 478 | (if (functionp electric-pair-skip-whitespace) |
| 479 | (funcall electric-pair-skip-whitespace) |
| 480 | electric-pair-skip-whitespace))) |
| 481 | (electric-pair--skip-whitespace)) |
| 482 | (eq (char-after) last-command-event)))) |
| 483 | ;; This is too late: rather than insert&delete we'd want to only |
| 484 | ;; skip (or insert in overwrite mode). The difference is in what |
| 485 | ;; goes in the undo-log and in the intermediate state which might |
| 486 | ;; be visible to other post-self-insert-hook. We'll just have to |
| 487 | ;; live with it for now. |
| 488 | (when skip-whitespace-info |
| 489 | (electric-pair--skip-whitespace)) |
| 490 | (delete-region (1- pos) (if (eq skip-whitespace-info 'chomp) |
| 491 | (point) |
| 492 | pos)) |
| 493 | (forward-char)) |
| 494 | ;; Insert matching pair. |
| 495 | ((and (memq syntax `(?\( ?\" ?\$)) |
| 496 | (not overwrite-mode) |
| 497 | (or unconditional |
| 498 | (not (funcall electric-pair-inhibit-predicate |
| 499 | last-command-event)))) |
| 500 | (save-excursion (electric-pair--insert pair))))) |
| 501 | (t |
| 502 | (when (and (if (functionp electric-pair-open-newline-between-pairs) |
| 503 | (funcall electric-pair-open-newline-between-pairs) |
| 504 | electric-pair-open-newline-between-pairs) |
| 505 | (eq last-command-event ?\n) |
| 506 | (< (1+ (point-min)) (point) (point-max)) |
| 507 | (eq (save-excursion |
| 508 | (skip-chars-backward "\t\s") |
| 509 | (char-before (1- (point)))) |
| 510 | (matching-paren (char-after)))) |
| 511 | (save-excursion (newline 1 t))))))) |
| 512 | |
| 513 | (put 'electric-pair-post-self-insert-function 'priority 20) |
| 514 | |
| 515 | (defun electric-pair-will-use-region () |
| 516 | (and (use-region-p) |
| 517 | (memq (car (electric-pair-syntax-info last-command-event)) |
| 518 | '(?\( ?\) ?\" ?\$)))) |
| 519 | |
| 520 | (defun electric-pair-delete-pair (arg &optional killp) |
| 521 | "When between adjacent paired delimiters, delete both of them. |
| 522 | ARG and KILLP are passed directly to |
| 523 | `backward-delete-char-untabify', which see." |
| 524 | (interactive "*p\nP") |
| 525 | (delete-char 1) |
| 526 | (backward-delete-char-untabify arg killp)) |
| 527 | |
| 528 | (defvar electric-pair-mode-map |
| 529 | (let ((map (make-sparse-keymap))) |
| 530 | (define-key map "\177" |
| 531 | `(menu-item |
| 532 | "" electric-pair-delete-pair |
| 533 | :filter |
| 534 | ,(lambda (cmd) |
| 535 | (let* ((prev (char-before)) |
| 536 | (next (char-after)) |
| 537 | (syntax-info (and prev |
| 538 | (electric-pair-syntax-info prev))) |
| 539 | (syntax (car syntax-info)) |
| 540 | (pair (cadr syntax-info))) |
| 541 | (and next pair |
| 542 | (memq syntax '(?\( ?\" ?\$)) |
| 543 | (eq pair next) |
| 544 | (if (functionp electric-pair-delete-adjacent-pairs) |
| 545 | (funcall electric-pair-delete-adjacent-pairs) |
| 546 | electric-pair-delete-adjacent-pairs) |
| 547 | cmd))))) |
| 548 | map) |
| 549 | "Keymap used by `electric-pair-mode'.") |
| 550 | |
| 551 | ;;;###autoload |
| 552 | (define-minor-mode electric-pair-mode |
| 553 | "Toggle automatic parens pairing (Electric Pair mode). |
| 554 | With a prefix argument ARG, enable Electric Pair mode if ARG is |
| 555 | positive, and disable it otherwise. If called from Lisp, enable |
| 556 | the mode if ARG is omitted or nil. |
| 557 | |
| 558 | Electric Pair mode is a global minor mode. When enabled, typing |
| 559 | an open parenthesis automatically inserts the corresponding |
| 560 | closing parenthesis. (Likewise for brackets, etc.)." |
| 561 | :global t :group 'electricity |
| 562 | (if electric-pair-mode |
| 563 | (progn |
| 564 | (add-hook 'post-self-insert-hook |
| 565 | #'electric-pair-post-self-insert-function) |
| 566 | (electric--sort-post-self-insertion-hook) |
| 567 | (add-hook 'self-insert-uses-region-functions |
| 568 | #'electric-pair-will-use-region)) |
| 569 | (remove-hook 'post-self-insert-hook |
| 570 | #'electric-pair-post-self-insert-function) |
| 571 | (remove-hook 'self-insert-uses-region-functions |
| 572 | #'electric-pair-will-use-region))) |
| 573 | |
| 574 | (provide 'elec-pair) |
| 575 | |
| 576 | ;;; elec-pair.el ends here |