From 28db4fa7510fe5846b13a104e35586ac9beb93fc Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Tue, 13 Nov 2018 11:57:13 +0100 Subject: [PATCH] update smartparens. Signed-off-by: Andreas Gerler --- bba.org | 6 +- .../smartparens-rust.el | 81 - .../smartparens-autoloads.el | 169 +- .../smartparens-clojure.el | 56 + .../smartparens-config.el | 89 +- .../smartparens-crystal.el | 261 + .../smartparens-elixir.el | 135 + .../smartparens-ess.el | 228 + .../smartparens-haskell.el | 31 +- .../smartparens-html.el | 17 +- .../smartparens-javascript.el | 55 + .../smartparens-latex.el | 105 +- .../smartparens-lua.el | 7 +- .../smartparens-markdown.el | 88 + .../smartparens-ml.el | 73 + .../smartparens-org.el | 66 + .../smartparens-pkg.el | 6 +- .../smartparens-python.el | 58 +- .../smartparens-racket.el | 3 +- .../smartparens-ruby.el | 244 +- .../smartparens-rust.el | 140 + .../smartparens-scala.el | 25 +- .../smartparens-text.el | 65 + .../smartparens.el | 5369 +++++++++++------ 24 files changed, 5089 insertions(+), 2288 deletions(-) delete mode 100644 elpa/smartparens-20160217.1102/smartparens-rust.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-autoloads.el (51%) create mode 100644 elpa/smartparens-20181028.1005/smartparens-clojure.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-config.el (54%) create mode 100644 elpa/smartparens-20181028.1005/smartparens-crystal.el create mode 100644 elpa/smartparens-20181028.1005/smartparens-elixir.el create mode 100644 elpa/smartparens-20181028.1005/smartparens-ess.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-haskell.el (72%) rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-html.el (94%) create mode 100644 elpa/smartparens-20181028.1005/smartparens-javascript.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-latex.el (55%) rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-lua.el (90%) create mode 100644 elpa/smartparens-20181028.1005/smartparens-markdown.el create mode 100644 elpa/smartparens-20181028.1005/smartparens-ml.el create mode 100644 elpa/smartparens-20181028.1005/smartparens-org.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-pkg.el (52%) rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-python.el (65%) rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-racket.el (95%) rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-ruby.el (63%) create mode 100644 elpa/smartparens-20181028.1005/smartparens-rust.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens-scala.el (69%) create mode 100644 elpa/smartparens-20181028.1005/smartparens-text.el rename elpa/{smartparens-20160217.1102 => smartparens-20181028.1005}/smartparens.el (62%) diff --git a/bba.org b/bba.org index 6a42a61a..f9096b0e 100644 --- a/bba.org +++ b/bba.org @@ -344,13 +344,13 @@ Some key bindings taken from [[https://ebzzry.github.io/emacs-pairs.html][emacs- #+BEGIN_SRC emacs-lisp :tangle yes (if (>= emacs-major-version 24) (progn - (add-to-list 'load-path "~/.emacs.d/elpa/smartparens-20160217.1102") - (add-to-list 'load-path "~/.emacs.d/elpa/dash-20180726.1213") + (add-to-list 'load-path "~/.emacs.d/elpa/smartparens-20181028.1005") + (add-to-list 'load-path "~/.emacs.d/elpa/dash-20180910.1856") (require 'smartparens) (smartparens-global-mode 1) (defmacro def-pairs (pairs) `(progn - ,@(loop for (key . val) in pairs + ,@(cl-loop for (key . val) in pairs collect `(defun ,(read (concat "wrap-with-" diff --git a/elpa/smartparens-20160217.1102/smartparens-rust.el b/elpa/smartparens-20160217.1102/smartparens-rust.el deleted file mode 100644 index 23ce71a8..00000000 --- a/elpa/smartparens-20160217.1102/smartparens-rust.el +++ /dev/null @@ -1,81 +0,0 @@ -;;; smartparens-rust.el --- Additional configuration for Haskell based modes. - -;; Copyright (C) 2015 Wilfred Hughes - -;; Created: 3 November 2015 -;; Keywords: abbrev convenience editing -;; URL: https://github.com/Fuco1/smartparens - -;; This file is not part of GNU Emacs. - -;;; License: - -;; This file is part of Smartparens. - -;; Smartparens 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 of the License, or -;; (at your option) any later version. - -;; Smartparens 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 Smartparens. If not, see . - -;;; Commentary: - -;; This file provides some additional configuration for Rust. To use -;; it, simply add: -;; -;; (require 'smartparens-config) -;; -;; alternatively, you can explicitly load these preferences: -;; -;; (require 'smartparens-rust) -;; -;; in your configuration. - -;; For more info, see github readme at -;; https://github.com/Fuco1/smartparens - -;;; Code: -(require 'smartparens) - -(defun sp-in-rust-lifetime-context (&rest args) - "Return t if point is in a Rust context where ' represents a lifetime. -If we return nil, ' should be used for character literals." - (or - (condition-case nil - ;; If point is just after a &', it's probably a &'foo. - (save-excursion - (backward-char 2) - (looking-at "&")) - ;; If we're at the beginning of the buffer, just carry on. - (beginning-of-buffer)) - ;; If point is inside < > it's probably a parameterised function. - (let ((paren-pos (nth 1 (syntax-ppss)))) - (and paren-pos - (save-excursion - (goto-char paren-pos) - (looking-at "<")))))) - -(defun sp-rust-could-be-parameterized (&rest args) - "Return t if we could add a in this position. -If nil, the user is probably using < for something else." - (and (apply #'sp-in-code-p args) - (looking-back (rx (or letter (seq letter "<") (seq letter "::<")))))) - -(sp-with-modes '(rust-mode) - (sp-local-pair "'" "'" :unless '(sp-in-comment-p sp-in-string-p sp-in-rust-lifetime-context)) - (sp-local-pair "<" ">" :when '(sp-rust-could-be-parameterized))) - -;; Rust has no sexp suffices. This fixes slurping -;; (|foo).bar -> (foo.bar) -(add-to-list 'sp-sexp-suffix (list #'rust-mode 'regexp "")) - -(provide 'smartparens-rust) - -;;; smartparens-rust.el ends here diff --git a/elpa/smartparens-20160217.1102/smartparens-autoloads.el b/elpa/smartparens-20181028.1005/smartparens-autoloads.el similarity index 51% rename from elpa/smartparens-20160217.1102/smartparens-autoloads.el rename to elpa/smartparens-20181028.1005/smartparens-autoloads.el index 17f722f2..9247c718 100644 --- a/elpa/smartparens-20160217.1102/smartparens-autoloads.el +++ b/elpa/smartparens-20181028.1005/smartparens-autoloads.el @@ -1,10 +1,12 @@ ;;; smartparens-autoloads.el --- automatically extracted autoloads ;; ;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + -;;;### (autoloads nil "smartparens" "smartparens.el" (22215 5349 -;;;;;; 0 0)) +;;;### (autoloads nil "smartparens" "smartparens.el" (0 0 0 0)) ;;; Generated autoloads from smartparens.el (autoload 'sp-cheat-sheet "smartparens" "\ @@ -12,7 +14,7 @@ Generate a cheat sheet of all the smartparens interactive functions. Without a prefix argument, print only the short documentation and examples. -With non-nil prefix argument, show the full documentation for each function. +With non-nil prefix argument ARG, show the full documentation for each function. You can follow the links to the function or variable help page. To get back to the full list, use \\[help-go-back]. @@ -29,15 +31,12 @@ better orientation. Keymap used for `smartparens-mode'.") (autoload 'sp-use-paredit-bindings "smartparens" "\ -Initiate `smartparens-mode-map' with paredit-compatible bindings for -corresponding functions provided by smartparens. See variable -`sp-paredit-bindings'. +Initiate `smartparens-mode-map' with `sp-paredit-bindings'. \(fn)" t nil) (autoload 'sp-use-smartparens-bindings "smartparens" "\ -Initiate `smartparens-mode-map' with smartparens bindings for navigation functions. -See variable `sp-smartparens-bindings'. +Initiate `smartparens-mode-map' with `sp-smartparens-bindings'. \(fn)" t nil) @@ -70,7 +69,8 @@ after the smartparens indicator in the mode list. (defvar smartparens-global-strict-mode nil "\ Non-nil if Smartparens-Global-Strict mode is enabled. -See the command `smartparens-global-strict-mode' for a description of this minor mode. +See the `smartparens-global-strict-mode' command +for a description of this minor mode. Setting this variable directly does not take effect; either customize it (see the info node `Easy Customization') or call the function `smartparens-global-strict-mode'.") @@ -94,9 +94,15 @@ Turn on `smartparens-strict-mode'. \(fn)" t nil) +(autoload 'turn-off-smartparens-strict-mode "smartparens" "\ +Turn off `smartparens-strict-mode'. + +\(fn)" t nil) + (defvar smartparens-global-mode nil "\ Non-nil if Smartparens-Global mode is enabled. -See the command `smartparens-global-mode' for a description of this minor mode. +See the `smartparens-global-mode' command +for a description of this minor mode. Setting this variable directly does not take effect; either customize it (see the info node `Easy Customization') or call the function `smartparens-global-mode'.") @@ -147,7 +153,8 @@ support custom pairs. (defvar show-smartparens-global-mode nil "\ Non-nil if Show-Smartparens-Global mode is enabled. -See the command `show-smartparens-global-mode' for a description of this minor mode. +See the `show-smartparens-global-mode' command +for a description of this minor mode. Setting this variable directly does not take effect; either customize it (see the info node `Easy Customization') or call the function `show-smartparens-global-mode'.") @@ -176,13 +183,140 @@ Turn off `show-smartparens-mode'. \(fn)" t nil) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens" '("smartparens-" "sp-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-clojure" "smartparens-clojure.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-clojure.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-clojure" '("sp-clojure-prefix"))) + +;;;*** + +;;;### (autoloads nil "smartparens-config" "smartparens-config.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-config.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-config" '("sp-lisp-invalid-hyperlink-p"))) + +;;;*** + +;;;### (autoloads nil "smartparens-crystal" "smartparens-crystal.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-crystal.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-crystal" '("sp-crystal-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-elixir" "smartparens-elixir.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-elixir.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-elixir" '("sp-elixir-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-ess" "smartparens-ess.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from smartparens-ess.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-ess" '("sp-ess-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-haskell" "smartparens-haskell.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-haskell.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-haskell" '("sp-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-html" "smartparens-html.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from smartparens-html.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-html" '("sp-html-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-latex" "smartparens-latex.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-latex.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-latex" '("sp-latex-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-lua" "smartparens-lua.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from smartparens-lua.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-lua" '("sp-lua-post-keyword-insert"))) + +;;;*** + +;;;### (autoloads nil "smartparens-markdown" "smartparens-markdown.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-markdown.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-markdown" '("sp-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-org" "smartparens-org.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from smartparens-org.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-org" '("sp--org-skip-asterisk"))) + +;;;*** + +;;;### (autoloads nil "smartparens-python" "smartparens-python.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-python.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-python" '("sp-python-fix-tripple-quotes"))) + +;;;*** + +;;;### (autoloads nil "smartparens-ruby" "smartparens-ruby.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from smartparens-ruby.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-ruby" '("sp-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-rust" "smartparens-rust.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from smartparens-rust.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-rust" '("sp-"))) + +;;;*** + +;;;### (autoloads nil "smartparens-scala" "smartparens-scala.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from smartparens-scala.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-scala" '("sp-scala-wrap-with-indented-newlines"))) + +;;;*** + +;;;### (autoloads nil "smartparens-text" "smartparens-text.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from smartparens-text.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "smartparens-text" '("sp-text-mode-"))) + ;;;*** -;;;### (autoloads nil nil ("smartparens-config.el" "smartparens-haskell.el" -;;;;;; "smartparens-html.el" "smartparens-latex.el" "smartparens-lua.el" -;;;;;; "smartparens-pkg.el" "smartparens-python.el" "smartparens-racket.el" -;;;;;; "smartparens-ruby.el" "smartparens-rust.el" "smartparens-scala.el") -;;;;;; (22215 5349 568964 0)) +;;;### (autoloads nil nil ("smartparens-javascript.el" "smartparens-ml.el" +;;;;;; "smartparens-pkg.el" "smartparens-racket.el") (0 0 0 0)) ;;;*** @@ -190,5 +324,6 @@ Turn off `show-smartparens-mode'. ;; version-control: never ;; no-byte-compile: t ;; no-update-autoloads: t +;; coding: utf-8 ;; End: ;;; smartparens-autoloads.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-clojure.el b/elpa/smartparens-20181028.1005/smartparens-clojure.el new file mode 100644 index 00000000..25b6d6d1 --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-clojure.el @@ -0,0 +1,56 @@ +;;; smartparens-clojure.el --- Additional configuration for Clojure mode. -*- lexical-binding: t; -*- +;; +;; Author: Vitalie Spinu +;; Maintainer: Matus Goljer +;; Created: 14 July 2016 +;; Keywords: abbrev convenience editing +;; URL: https://github.com/Fuco1/smartparens +;; +;; This file is not part of GNU Emacs. +;; +;;; License: +;; +;; This file is part of Smartparens. +;; +;; Smartparens 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 of the License, or +;; (at your option) any later version. +;; +;; Smartparens 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 Smartparens. If not, see . +;; +;;; Commentary: +;; +;; This file provides some additional configuration for Clojure mode. To use +;; it, simply add: +;; +;; (require 'smartparens-clojure) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;;; Code: + +(require 'smartparens) + +(defvar sp-clojure-prefix "\\(?:[@`'#~,_?^]+\\)" + "Prefix used in `sp-sepx-prefix' for clojure modes.") + +(dolist (mode '(clojure-mode clojurescript-mode clojurec-mode cider-repl-mode)) + (add-to-list 'sp-sexp-prefix `(,mode regexp ,sp-clojure-prefix))) + +;; Match "`" with "`" in strings and comments +(sp-with-modes sp-clojure-modes + (sp-local-pair "`" "`" + :when '(sp-in-string-p + sp-in-comment-p) + :unless '(sp-lisp-invalid-hyperlink-p))) + +(provide 'smartparens-clojure) +;;; smartparens-clojure.el ends here diff --git a/elpa/smartparens-20160217.1102/smartparens-config.el b/elpa/smartparens-20181028.1005/smartparens-config.el similarity index 54% rename from elpa/smartparens-20160217.1102/smartparens-config.el rename to elpa/smartparens-20181028.1005/smartparens-config.el index 2674f7da..2b8665c4 100644 --- a/elpa/smartparens-20160217.1102/smartparens-config.el +++ b/elpa/smartparens-20181028.1005/smartparens-config.el @@ -1,4 +1,4 @@ -;;; smartparens-config.el --- Default configuration for smartparens package +;;; smartparens-config.el --- Default configuration for smartparens package -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2016 Matus Goljer @@ -45,52 +45,51 @@ (require 'smartparens) -;; do not autoinsert ' pair if the point is preceeded by word. This -;; will handle the situation when ' is used as a contraction symbol in -;; natural language. Nil for second argument means to keep the -;; original definition of closing pair. -(sp-pair "'" nil :unless '(sp-point-after-word-p)) - -(defun sp-lisp-invalid-hyperlink-p (_1 action _2) +(defun sp-lisp-invalid-hyperlink-p (_id action _context) + "Test if there is an invalid hyperlink in a Lisp docstring. +ID, ACTION, CONTEXT." (when (eq action 'navigate) ;; Ignore errors due to us being at the start or end of the ;; buffer. (ignore-errors - (or (and (looking-at "\\sw\\|\\s_") - (save-excursion - (backward-char 2) - (looking-at "\\sw\\|\\s_"))) - (and (save-excursion - (backward-char 1) - (looking-at "\\sw\\|\\s_")) - (save-excursion - (forward-char 1) - (looking-at "\\sw\\|\\s_"))))))) + (or + ;; foo'|bar + (and (looking-at "\\sw\\|\\s_") + ;; do not consider punctuation + (not (looking-at "[?.,;!]")) + (save-excursion + (backward-char 2) + (looking-at "\\sw\\|\\s_"))) + ;; foo|'bar + (and (save-excursion + (backward-char 1) + (looking-at "\\sw\\|\\s_")) + (save-excursion + (forward-char 1) + (looking-at "\\sw\\|\\s_") + ;; do not consider punctuation + (not (looking-at "[?.,;!]")))))))) ;; emacs is lisp hacking enviroment, so we set up some most common ;; lisp modes too (sp-with-modes sp-lisp-modes ;; disable ', it's the quote character! - (sp-local-pair "'" nil :actions nil) + (sp-local-pair "'" nil :actions nil)) + +(sp-with-modes (-difference sp-lisp-modes sp-clojure-modes) ;; also only use the pseudo-quote inside strings where it serve as ;; hyperlink. (sp-local-pair "`" "'" :when '(sp-in-string-p sp-in-comment-p) :unless '(sp-lisp-invalid-hyperlink-p) - :skip-match (lambda (ms mb me) + :skip-match (lambda (ms _mb _me) (cond ((equal ms "'") (or (sp-lisp-invalid-hyperlink-p "`" 'navigate '_) (not (sp-point-in-string-or-comment)))) (t (not (sp-point-in-string-or-comment))))))) -;; Unfortunately emacs devs in their glorious wisdom decided to -;; make @ no longer have prefix syntax, it is now a symbol... because -;; apparently its use in symbols is so frequent. Anyway, since we -;; can't really change that, let's use a regexp based solution -(add-to-list 'sp-sexp-prefix (list 'emacs-lisp-mode 'regexp "\\(?:,@\\|[',`]\\)")) - ;; TODO: this should only be active in docstring, otherwise we want ;; the regexp completion \\{\\}. To handle this feature, we must ;; allow multiple pairs on same opening (therefore, the unique ID must @@ -102,21 +101,31 @@ ;; automatically. If you want to call sp-local-pair outside this ;; macro, you MUST supply the major mode argument. +(eval-after-load 'clojure-mode '(require 'smartparens-clojure)) +(eval-after-load 'crystal-mode '(require 'smartparens-crystal)) +(eval-after-load 'elixir-mode '(require 'smartparens-elixir)) +(eval-after-load 'enh-ruby-mode '(require 'smartparens-ruby)) +(eval-after-load 'ess '(require 'smartparens-ess)) +(eval-after-load 'haskell-interactive-mode '(require 'smartparens-haskell)) +(eval-after-load 'haskell-mode '(require 'smartparens-haskell)) (--each sp--html-modes - (eval-after-load (symbol-name it) '(require 'smartparens-html))) -(eval-after-load "latex" '(require 'smartparens-latex)) -(eval-after-load "tex-mode" '(require 'smartparens-latex)) -(eval-after-load "lua-mode" '(require 'smartparens-lua)) -(eval-after-load "ruby-mode" '(require 'smartparens-ruby)) -(eval-after-load "enh-ruby-mode" '(require 'smartparens-ruby)) -(eval-after-load "rust-mode" '(require 'smartparens-rust)) -(eval-after-load "haskell-mode" '(require 'smartparens-haskell)) -(eval-after-load "haskell-interactive-mode" '(require 'smartparens-haskell)) -(--each '("python-mode" "python") - (eval-after-load it '(require 'smartparens-python))) -(eval-after-load "scala-mode" '(require 'smartparens-scala)) -(eval-after-load "racket-mode" '(require 'smartparens-racket)) - + (eval-after-load it '(require 'smartparens-html))) +(eval-after-load 'latex '(require 'smartparens-latex)) +(eval-after-load 'lua-mode '(require 'smartparens-lua)) +(eval-after-load 'markdown-mode '(require 'smartparens-markdown)) +(--each '(python-mode python) + (eval-after-load it '(require 'smartparens-python))) +(eval-after-load 'org '(require 'smartparens-org)) +(eval-after-load 'racket-mode '(require 'smartparens-racket)) +(eval-after-load 'ruby-mode '(require 'smartparens-ruby)) +(eval-after-load 'rust-mode '(require 'smartparens-rust)) +(eval-after-load 'scala-mode '(require 'smartparens-scala)) +(eval-after-load 'tex-mode '(require 'smartparens-latex)) +(eval-after-load 'text-mode '(require 'smartparens-text)) +(eval-after-load 'tuareg '(require 'smartparens-ml)) +(eval-after-load 'fsharp-mode '(require 'smartparens-ml)) +(--each '(js js2-mode) + (eval-after-load it '(require 'smartparens-javascript))) (provide 'smartparens-config) ;;; smartparens-config.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-crystal.el b/elpa/smartparens-20181028.1005/smartparens-crystal.el new file mode 100644 index 00000000..574ac8ea --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-crystal.el @@ -0,0 +1,261 @@ +;;; smartparens-crystal.el --- Additional configuration for Crystal based modes. -*- lexical-binding: t; -*- + +;; Copyright (C) 2018 Brantou + +;; Author: Brantou +;; Maintainer: Brantou +;; Created: 5 March 2018 +;; Keywords: abbrev convenience editing +;; URL: https://github.com/Fuco1/smartparens + +;; Based on smartparens-ruby.el + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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 of the License, or +;; (at your option) any later version. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for Crystal based +;; modes. To use it, simply add: +;; +;; (require 'smartparens-crystal) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; + +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. + +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) +(require 'smartparens-ruby) + +(declare-function crystal-forward-sexp "crystal") +(declare-function crystal-backward-sexp "crystal") + +(defun sp-crystal-forward-sexp () + "Wrapper for `crystal-forward-sexp'." + (interactive) + (crystal-forward-sexp)) + +(defun sp-crystal-backward-sexp () + "Wrapper for `crystal-backward-sexp'." + (interactive) + (crystal-backward-sexp)) + +(defun sp-crystal-inline-p (id) + "Test if ID is inline." + (save-excursion + (when (looking-back id nil) + (backward-word)) + (when (not (or (looking-back "^[[:blank:]]*" nil) + (looking-back "= *" nil))) + (or (save-excursion + (forward-symbol -1) + (forward-symbol 1) + (looking-at-p (concat " *" id))) + (save-excursion + ;; This does not seem to make emacs snapshot happy + (ignore-errors + (sp-crystal-backward-sexp) + (sp-crystal-forward-sexp) + (looking-at-p (concat "[^[:blank:]]* *" id)))))))) + +(defun sp-crystal-skip-inline-match-p (ms _mb _me) + "If non-nil, skip inline match. +MS, MB, ME." + (or (sp-ruby-method-p ms) + (sp-crystal-inline-p ms))) + +(defun sp-crystal-in-string-word-or-inline-p (id action context) + "Test if point is inside string, word or inline. +ID, ACTION, CONTEXT." + (or (sp-ruby-in-string-or-word-p id action context) + (and (looking-back id nil) + (sp-crystal-inline-p id)))) + +(add-to-list 'sp-navigate-skip-match + '((crystal-mode) . sp--ruby-skip-match)) + +(dolist (mode '(crystal-mode)) + (add-to-list 'sp-sexp-suffix `(,mode syntax ""))) + +(sp-with-modes 'crystal-mode + (sp-local-pair "do" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-block-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "{" "}" + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-post-handler) + :suffix "") + + (sp-local-pair "begin" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-block-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "def" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "class" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "struct" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "lib" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "fun" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "enum" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "union" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "module" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "macro" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "case" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-ruby-skip-method-p + :suffix "") + + (sp-local-pair "if" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-crystal-in-string-word-or-inline-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-crystal-skip-inline-match-p + :suffix "") + + (sp-local-pair "unless" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-crystal-in-string-word-or-inline-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-crystal-skip-inline-match-p + :suffix "") + + (sp-local-pair "while" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-crystal-in-string-word-or-inline-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-crystal-skip-inline-match-p + :suffix "") + + (sp-local-pair "until" "end" + :when '(("SPC" "RET" "")) + :unless '(sp-crystal-in-string-word-or-inline-p sp-in-comment-p) + :actions '(insert navigate) + :pre-handlers '(sp-ruby-pre-handler) + :post-handlers '(sp-ruby-def-post-handler) + :skip-match 'sp-crystal-skip-inline-match-p + :suffix "") + + (sp-local-pair "|" "|" + :when '(sp-ruby-should-insert-pipe-close) + :pre-handlers '(sp-ruby-pre-pipe-handler) + :suffix "")) + +(provide 'smartparens-crystal) + +;;; smartparens-crystal.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-elixir.el b/elpa/smartparens-20181028.1005/smartparens-elixir.el new file mode 100644 index 00000000..8411162c --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-elixir.el @@ -0,0 +1,135 @@ +;;; smartparens-elixir.el --- Configuration for Elixir. -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Matúš Goljer + +;; Author: Matúš Goljer +;; Maintainer: Matúš Goljer +;; Version: 0.0.1 +;; Created: 15th January 2017 +;; Keywords: languages + +;; 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 +;; of the License, 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 this program. If not, see . + +;;; Commentary: + +;;; Code: + +(require 'smartparens) + +(--each '(elixir-mode) + (add-to-list 'sp-sexp-suffix (list it 'regexp ""))) + +(defun sp-elixir-def-p (id) + "Return non-nil if the \"do\" keyword is part of definition. + +ID is the opening delimiter. + +Definitions are the constructions of the form defmodule-do-end, +def-do-end and similar pairs." + (save-excursion + (when (equal "do" id) + (back-to-indentation) + (looking-at (regexp-opt '( + "defmodule" + "defmacro" + "defmacrop" + "quote" + "def" + "defp" + "if" + "unless" + "case" + "cond" + "with" + "for" + "receive" + "try" + )))))) + +(defun sp-elixir-skip-def-p (ms _mb _me) + "Test if \"do\" is part of definition. +MS, MB, ME." + (sp-elixir-def-p ms)) + +(defun sp-elixir-do-block-post-handler (_id action _context) + "Insert \"do\" keyword and indent the new block. +ID, ACTION, CONTEXT." + (when (eq action 'insert) + (let ((m (make-marker))) + (save-excursion + (forward-word) ;; over the "end" + (move-marker m (point))) + (save-excursion (newline)) + (save-excursion (insert " do")) + (indent-region (line-beginning-position) m) + (move-marker m nil nil)))) + +(defun sp-elixir-empty-do-block-post-handler (_id action _context) + "Insert empty \"do\" keyword and indent the new block. + +This is used for receive-do-end expression. +ID, ACTION, CONTEXT." + (when (eq action 'insert) + (let ((m (make-marker))) + (save-excursion + (forward-word) ;; over the "end" + (move-marker m (point))) + (save-excursion + (forward-line -1) + (end-of-line) + (insert " do")) + (save-excursion (newline)) + (indent-region (line-beginning-position) m) + (indent-according-to-mode) + (move-marker m nil nil)))) + +(sp-with-modes 'elixir-mode + (sp-local-pair "do" "end" + :when '(("SPC" "RET" "")) + :skip-match 'sp-elixir-skip-def-p + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "def" "end" + :when '(("SPC" "RET" "")) + :post-handlers '(sp-elixir-do-block-post-handler) + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "defp" "end" + :when '(("SPC" "RET" "")) + :post-handlers '(sp-elixir-do-block-post-handler) + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "defmodule" "end" + :when '(("SPC" "RET" "")) + :post-handlers '(sp-elixir-do-block-post-handler) + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "fn" "end" + :when '(("SPC" "RET" "")) + :post-handlers '("| ")) + (sp-local-pair "if" "end" + :when '(("SPC" "RET" "")) + :post-handlers '(sp-elixir-do-block-post-handler) + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "unless" "end" + :when '(("SPC" "RET" "")) + :post-handlers '(sp-elixir-do-block-post-handler) + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "case" "end" + :when '(("SPC" "RET" "")) + :post-handlers '(sp-elixir-do-block-post-handler) + :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "receive" "end" + :when '(("RET" "")) + :post-handlers '(sp-elixir-empty-do-block-post-handler)) + ) + +(provide 'smartparens-elixir) +;;; smartparens-elixir.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-ess.el b/elpa/smartparens-20181028.1005/smartparens-ess.el new file mode 100644 index 00000000..3bac0b5f --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-ess.el @@ -0,0 +1,228 @@ +;;; smartparens-ess.el --- Smartparens Extension for Emacs Speaks Statistics -*- lexical-binding: t; -*- + +;; Copyright (c) 2015-2016 Bernhard Pröll + +;; Author: Bernhard Pröll +;; Maintainer: Bernhard Pröll +;; URL: https://github.com/Fuco1/smartparens +;; Created: 2015-02-26 +;; Version: 0.2 +;; Keywords: abbrev convenience editing + +;; This file is NOT part of GNU Emacs. + +;; 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 of the License, 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 this program. If not, see . + +;;; Commentary: +;; +;; This file provides some additional configuration for ESS. To use +;; it, simply add: +;; +;; (require 'smartparens-ess) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;;; Code: + +(require 'smartparens) +(require 'rx) + +(defvar ess-roxy-str) + +(declare-function ess-roxy-indent-on-newline "ess-roxy") + + +;; avoid traveling commas when slurping +;; (|a, b), c ---> (|a, b, c) +(dolist (mode '(ess-mode inferior-ess-mode)) + (add-to-list 'sp-sexp-suffix (list mode 'regexp ""))) + +;; `sp-sexp-prefix' for ESS +(add-to-list 'sp-sexp-prefix + (list 'ess-mode 'regexp + (rx (zero-or-more (or word (syntax symbol)))))) + +;; slurping follows Google's R style guide +;; see https://google.github.io/styleguide/Rguide.xml +(defun sp-ess-pre-handler (_id action _context) + "Remove spaces before opening parenthesis in a function call. +Remove redundant space around commas. +ID, ACTION, CONTEXT." + (when (equal action 'slurp-forward) + (let ((sxp (sp-get-thing 'back))) + (save-excursion + (goto-char (sp-get sxp :beg-prf)) + ;; (|) x ---> (x) + (when (looking-back (rx (syntax open-parenthesis) + (one-or-more space)) nil) + (cycle-spacing 0 nil 'single-shot)) + (cond + ;; (|)if(cond) ---> (|if (cond)) + ((member (sp-get sxp :prefix) '("if" "for" "while")) + (goto-char (sp-get sxp :beg)) + (cycle-spacing 1 nil 'single-shot)) + ;; (|)v [,2] <- if(x > 1) ---> (v[,2] <- if (x > 1)) + ((and + (member (sp-get sxp :op) '("[" "(")) + (equal (sp-get sxp :prefix) "") + (looking-back + (rx (and (not-char "%" ",") + (not (syntax close-parenthesis))) + (one-or-more space)) nil) + (not (member + (save-excursion + (sp-backward-sexp) + (thing-at-point 'word 'noprop)) + '("if" "for" "while")))) + (cycle-spacing 0 nil 'single-shot)) + ;; (|[...])%in% ---> ([...] %in%|) + ((or (looking-at "%") (looking-back "%" nil)) + (just-one-space)) + ;; (|)a , b, c ---> (|a, b, c) + ((looking-back + (rx (zero-or-more space) "," (zero-or-more space)) + (line-beginning-position) 'greedy) + (replace-match ", ")))))) + (when (equal action 'slurp-backward) + (let ((sxp (sp-get-thing))) + (save-excursion + (goto-char (sp-get sxp :end)) + ;; x (|) ---> (x) + (when (looking-at (rx (one-or-more space) + (syntax close-parenthesis))) + (cycle-spacing 0 nil 'single-shot)) + ;; if(cond){} (|) ---> (if (cond) {}|) + (cond ((member (sp-get sxp :prefix) '("if" "for" "while")) + (goto-char (sp-get sxp :beg)) + (cycle-spacing 1 nil 'single-shot)) + ;; for style reasons there should be a space before curly + ;; brackets and binary operators + ((and (member (sp-get sxp :op) '("{" "%")) + (not (looking-at (rx (syntax close-parenthesis))))) + (cycle-spacing 1 nil 'single-shot)) + ;; v[2](|) ---> (v[2]|) + ((and + (not (member (thing-at-point 'word 'noprop) + '("if" "for" "while"))) + (looking-at + (rx (and (zero-or-more space) + (not-char "{") + (or (syntax close-parenthesis) + (char "(") + (char "[")))))) + (cycle-spacing 0 nil 'single-shot)) + ;; 1 , 2 (|) ---> (1, 2) + ((looking-at + (rx (zero-or-more space) "," (zero-or-more space))) + (replace-match ", "))))))) + +;; function(x) {|} ---> function(x) {\n|\n} +;; ##' \tabular{rrr}{|} ---> +;; ##' \tabular{rrr}{ +;; ##' | +;; ##' } +(defun sp-ess-open-sexp-indent (&rest _args) + "Open new brace or bracket with indentation. +ARGS." + (if (and (fboundp 'ess-roxy-entry-p) (ess-roxy-entry-p)) + (progn + (save-excursion (ess-roxy-indent-on-newline)) + (when (looking-back ess-roxy-str nil) + (cycle-spacing 3 nil t))) + (newline) + (indent-according-to-mode) + (forward-line -1) + (indent-according-to-mode))) + +(defun sp-ess-roxy-str-p (_id action _context) + "Test if looking back at `ess-roxy-re'. +ID, ACTION, CONTEXT." + (when (and (boundp 'ess-roxy-re) (eq action 'insert)) + (sp--looking-back-p ess-roxy-re))) + +(sp-with-modes 'ess-mode + (sp-local-pair "{" nil + :pre-handlers '(sp-ess-pre-handler) + ;; the more reasonable C-j interferes with default binding for + ;; `ess-eval-line' + :post-handlers '((sp-ess-open-sexp-indent "M-j"))) + (sp-local-pair "(" nil + :pre-handlers '(sp-ess-pre-handler) + :post-handlers '((sp-ess-open-sexp-indent "M-j"))) + (sp-local-pair "[" nil + :pre-handlers '(sp-ess-pre-handler) + :post-handlers '((sp-ess-open-sexp-indent "M-j"))) + (sp-local-pair "'" nil + :unless '(sp-ess-roxy-str-p sp-in-comment-p sp-in-string-quotes-p))) + +;;; roxygen2 markup +;; see https://cran.r-project.org/web/packages/roxygen2/vignettes/formatting.html +(sp-with-modes 'ess-mode + (sp-local-pair "\\strong{" "}" + :when '(sp-in-comment-p) + :trigger "\\strong") + (sp-local-pair "\\emph{" "}" + :when '(sp-in-comment-p) + :trigger "\\emph") + (sp-local-pair "\\code{" "}" + :when '(sp-in-comment-p) + :trigger "\\code") + (sp-local-pair "\\url{" "}" + :when '(sp-in-comment-p) + :trigger "\\url") + (sp-local-pair "\\link{" "}" + :when '(sp-in-comment-p) + :trigger "\\link") + (sp-local-pair "\\href{" "}" + :when '(sp-in-comment-p) + :trigger "\\href" + :suffix "{[^}]*}") + (sp-local-pair "\\email{" "}" + :when '(sp-in-comment-p) + :trigger "\\email") + (sp-local-pair "\\pkg{" "}" + :when '(sp-in-comment-p) + :trigger "\\pkg") + (sp-local-pair "\\item{" "}" + :when '(sp-in-comment-p) + :post-handlers '((sp-ess-open-sexp-indent "M-j")) + :trigger "\\item{") + (sp-local-pair "\\enumerate{" "}" + :when '(sp-in-comment-p) + :post-handlers '((sp-ess-open-sexp-indent "M-j")) + :trigger "\\enumerate") + (sp-local-pair "\\itemize{" "}" + :when '(sp-in-comment-p) + :post-handlers '((sp-ess-open-sexp-indent "M-j")) + :trigger "\\itemize") + (sp-local-pair "\\describe{" "}" + :when '(sp-in-comment-p) + :post-handlers '((sp-ess-open-sexp-indent "M-j")) + :trigger "\\describe") + (sp-local-pair "\\eqn{" "}" + :when '(sp-in-comment-p) + :trigger "\\eqn") + (sp-local-pair "\\deqn{" "}" + :when '(sp-in-comment-p) + :trigger "\\deqn") + (sp-local-pair "\\tabular{" "}" + :when '(sp-in-comment-p) + :trigger "\\tabular" + :post-handlers '((sp-ess-open-sexp-indent "M-j")) + :suffix "{[^}]*}")) + + +(provide 'smartparens-ess) +;;; smartparens-ess ends here diff --git a/elpa/smartparens-20160217.1102/smartparens-haskell.el b/elpa/smartparens-20181028.1005/smartparens-haskell.el similarity index 72% rename from elpa/smartparens-20160217.1102/smartparens-haskell.el rename to elpa/smartparens-20181028.1005/smartparens-haskell.el index 4219979b..47a9ab58 100644 --- a/elpa/smartparens-20160217.1102/smartparens-haskell.el +++ b/elpa/smartparens-20181028.1005/smartparens-haskell.el @@ -1,4 +1,4 @@ -;;; smartparens-haskell.el --- Additional configuration for Haskell based modes. +;;; smartparens-haskell.el --- Additional configuration for Haskell based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2015 Michael Xavier @@ -46,9 +46,36 @@ ;;; Code: (require 'smartparens) +(defun sp-haskell-skip-apostrophe (_ms mb _me) + (save-excursion + ;; We test the syntax class because haskell mode overrides + ;; the class for ' on the fly when it run the syntax pass of + ;; font-lock... so that if '' is a valid string (char) it + ;; gets an override via 'syntax-table property. In which + ;; case we just agree with haskell mode and let it be used as + ;; a pair. + (not (eq (syntax-class (syntax-after mb)) 7)))) + +(defun sp-haskell-strict-ignore-apostrophe-after-word (_id action _context) + "Ignore trailing ' when navigating. + +Because ' in haskell is symbol class it gets picked up as part of +a words such as myFunction', and then strict mode won't allow us +to delete it. Also show-smartparens-mode incorrectly highlights +it as missing an opener. + +So we ignore that pair when at the end of word." + (when (eq action 'navigate) + (sp--looking-back-p (concat "\\(\\sw\\|\\s_\\)'+")))) + (sp-with-modes '(haskell-mode haskell-interactive-mode) + (sp-local-pair "{-" "-}") (sp-local-pair "{-#" "#-}") - (sp-local-pair "'" nil :unless '(sp-point-after-word-p)) + (sp-local-pair "{-@" "@-}") + (sp-local-pair "'" nil + :unless '(sp-point-after-word-p + sp-haskell-strict-ignore-apostrophe-after-word) + :skip-match 'sp-haskell-skip-apostrophe) (sp-local-pair "\\(" nil :actions nil)) (defun sp--inferior-haskell-mode-backward-bound-fn () diff --git a/elpa/smartparens-20160217.1102/smartparens-html.el b/elpa/smartparens-20181028.1005/smartparens-html.el similarity index 94% rename from elpa/smartparens-20160217.1102/smartparens-html.el rename to elpa/smartparens-20181028.1005/smartparens-html.el index db88ca02..3ba64060 100644 --- a/elpa/smartparens-20160217.1102/smartparens-html.el +++ b/elpa/smartparens-20181028.1005/smartparens-html.el @@ -1,4 +1,4 @@ -;;; smartparens-html.el --- Additional configuration for HTML based modes. +;;; smartparens-html.el --- Additional configuration for HTML based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2014 Matus Goljer @@ -37,12 +37,6 @@ ;; into your configuration. You can use this in conjunction with the ;; default config or your own configuration. -;; It is advised that you add `html-mode' to the list -;; `sp-navigate-consider-stringlike-sexp'. This will tell smartparens -;; to treat the "" delimited strings as sexps, and enable you to use -;; all the sexp-based commands on them (such as `sp-down-sexp', -;; `sp-up-sexp' etc.) - ;; This file provides these interactive functions: ;; `sp-html-next-tag' - Recommended binding: C-c C-f @@ -95,7 +89,11 @@ backward." (sp-beginning-of-sexp arg) (sp-beginning-of-sexp (1- (- (prefix-numeric-value arg))))))) -(defun sp-html-post-handler (&optional id action context) +(defun sp-html-post-handler (&optional _id action _context) + "Post-action hooks for `html-mode'. + +ID is the tag being processed, ACTION is the action and CONTEXT +specifies if we are inside a string or code." (cl-case action (slurp-forward (save-excursion @@ -167,6 +165,9 @@ backward." (--each sp--html-modes (add-to-list 'sp-navigate-consider-sgml-tags it)) +(--each '(web-mode) + (add-to-list 'sp-sexp-suffix (list it 'regexp ""))) + (provide 'smartparens-html) ;;; smartparens-html.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-javascript.el b/elpa/smartparens-20181028.1005/smartparens-javascript.el new file mode 100644 index 00000000..5ec57462 --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-javascript.el @@ -0,0 +1,55 @@ +;;; smartparens-javascript.el --- Additional configuration for JavaScript based modes. -*- lexical-binding: t; -*- + +;; Copyright (c) 2017 Marinin Tim +;; Author: Tim Marinin +;; Maintainer: Tim Marinin +;; Created: 2017-03-03 +;; Keywords: abbrev convenience editing javascript +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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 of the License, or +;; (at your option) any later version. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for JavaScript based +;; modes. To use it, simply add: +;; +;; (require 'smartparens-javascript) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. +;; +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) + +;; (|sys).path.append---the dot should not travel with the closing +;; paren +(--each '(js-mode javascript-mode js2-mode typescript-mode rjsx-mode) + (add-to-list 'sp-sexp-suffix (list it 'regexp ""))) + +(provide 'smartparens-javascript) +;;; smartparens-javasript.el ends here diff --git a/elpa/smartparens-20160217.1102/smartparens-latex.el b/elpa/smartparens-20181028.1005/smartparens-latex.el similarity index 55% rename from elpa/smartparens-20160217.1102/smartparens-latex.el rename to elpa/smartparens-20181028.1005/smartparens-latex.el index 8d958580..cad25373 100644 --- a/elpa/smartparens-20160217.1102/smartparens-latex.el +++ b/elpa/smartparens-20181028.1005/smartparens-latex.el @@ -1,4 +1,4 @@ -;;; smartparens-latex.el --- Additional configuration for (La)TeX based modes. +;;; smartparens-latex.el --- Additional configuration for (La)TeX based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2016 Matus Goljer @@ -37,12 +37,6 @@ ;; into your configuration. You can use this in conjunction with the ;; default config or your own configuration. -;; It is advised that you add `latex-mode' to the list -;; `sp-navigate-consider-stringlike-sexp'. This will tell -;; smartparens to treat the $$ math blocks as sexps, and enable you -;; to use all the sexp-based commands on them (such as -;; `sp-down-sexp', `sp-up-sexp' etc.) - ;; If you have good ideas about what should be added please file an ;; issue on the github tracker. @@ -53,30 +47,43 @@ (require 'smartparens) -(defun sp-latex-insert-spaces-inside-pair (id action context) +(defun sp-latex-insert-spaces-inside-pair (_id action _context) + "ID, ACTION, CONTEXT." (when (eq action 'insert) (insert " ") - (backward-char 1))) + (backward-char 1)) + (when (and (eq action 'wrap) + (save-excursion + (goto-char (sp-get sp-last-wrapped-region :beg-in)) + (not (sp--looking-back-p "[[{(]")))) + (save-excursion + (goto-char (sp-get sp-last-wrapped-region :end-in)) + (insert " ") + (goto-char (sp-get sp-last-wrapped-region :beg-in)) + (insert " ")))) -(defun sp-latex-skip-match-apostrophe (ms mb me) +(defun sp-latex-skip-match-apostrophe (ms _mb me) + "MS, MB, ME." (when (equal ms "'") (save-excursion (goto-char me) (looking-at-p "\\sw")))) -(defun sp-latex-skip-double-quote (_1 action _2) +(defun sp-latex-skip-double-quote (_id action _context) + "ID, ACTION, CONTEXT." (when (eq action 'insert) (when (looking-at-p "''''") (delete-char -2) (delete-char 2) (forward-char 2)))) -(defun sp-latex-point-after-backslash (id action context) +(defun sp-latex-point-after-backslash (id action _context) "Return t if point follows a backslash, nil otherwise. -This predicate is only tested on \"insert\" action." +This predicate is only tested on \"insert\" action. +ID, ACTION, CONTEXT." (when (eq action 'insert) (let ((trigger (sp-get-pair id :trigger))) - (looking-back (concat "\\\\" (regexp-quote (if trigger trigger id))))))) + (looking-back (concat "\\\\" (regexp-quote (if trigger trigger id))) nil)))) (add-to-list 'sp-navigate-skip-match '((tex-mode plain-tex-mode latex-mode) . sp--backslash-skip-match)) @@ -85,6 +92,7 @@ This predicate is only tested on \"insert\" action." tex-mode plain-tex-mode latex-mode + LaTeX-mode ) (sp-local-pair "`" "'" :actions '(:rem autoskip) @@ -93,9 +101,10 @@ This predicate is only tested on \"insert\" action." ;; math modes, yay. The :actions are provided automatically if ;; these pairs do not have global definitions. (sp-local-pair "$" "$") - (sp-local-pair "\\[" "\\]" :unless '(sp-latex-point-after-backslash)) - ;; disable useless pairs. Maybe also remove " ' and \"? - (sp-local-pair "/*" nil :actions nil) + (sp-local-pair "\\[" "\\]" + :unless '(sp-latex-point-after-backslash)) + + ;; disable useless pairs. (sp-local-pair "\\\\(" nil :actions nil) (sp-local-pair "'" nil :actions nil) (sp-local-pair "\\\"" nil :actions nil) @@ -110,6 +119,11 @@ This predicate is only tested on \"insert\" action." ;; add the prefix function sticking to {} pair (sp-local-pair "{" nil :prefix "\\\\\\(\\sw\\|\\s_\\)*") + ;; do not add more space when slurping + (sp-local-pair "{" "}") + (sp-local-pair "(" ")") + (sp-local-pair "[" "]") + ;; pairs for big brackets. Needs more research on what pairs are ;; useful to add here. Post suggestions if you know some. (sp-local-pair "\\left(" "\\right)" @@ -124,21 +138,48 @@ This predicate is only tested on \"insert\" action." :trigger "\\l{" :when '(sp-in-math-p) :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\bigl(" "\\bigr)" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\biggl(" "\\biggr)" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\Bigl(" "\\Bigr)" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\Biggl(" "\\Biggr)" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\bigl[" "\\bigr]" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\biggl[" "\\biggr]" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\Bigl[" "\\Bigr]" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\Biggl[" "\\Biggr]" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\bigl\\{" "\\bigr\\}" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\biggl\\{" "\\biggr\\}" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\Bigl\\{" "\\Bigr\\}" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\Biggl\\{" "\\Biggr\\}" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\lfloor" "\\rfloor" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\lceil" "\\rceil" :post-handlers '(sp-latex-insert-spaces-inside-pair)) - (sp-local-pair "\\langle" "\\rangle" :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\left|" "\\right|" + :trigger "\\l|" + :when '(sp-in-math-p) + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\bigl(" "\\bigr)" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\biggl(" "\\biggr)" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\Bigl(" "\\Bigr)" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\Biggl(" "\\Biggr)" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\bigl[" "\\bigr]" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\biggl[" "\\biggr]" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\Bigl[" "\\Bigr]" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\Biggl[" "\\Biggr]" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\bigl\\{" "\\bigr\\}" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\biggl\\{" "\\biggr\\}" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\Bigl\\{" "\\Bigr\\}" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\Biggl\\{" "\\Biggr\\}" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\lfloor" "\\rfloor" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\lceil" "\\rceil" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\langle" "\\rangle" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\lVert" "\\rVert" + :when '(sp-in-math-p) + :trigger "\\lVert" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) + (sp-local-pair "\\lvert" "\\rvert" + :when '(sp-in-math-p) + :trigger "\\lvert" + :post-handlers '(sp-latex-insert-spaces-inside-pair)) ;; some common wrappings (sp-local-tag "\"" "``" "''" :actions '(wrap)) diff --git a/elpa/smartparens-20160217.1102/smartparens-lua.el b/elpa/smartparens-20181028.1005/smartparens-lua.el similarity index 90% rename from elpa/smartparens-20160217.1102/smartparens-lua.el rename to elpa/smartparens-20181028.1005/smartparens-lua.el index c322a578..29621a6c 100644 --- a/elpa/smartparens-20160217.1102/smartparens-lua.el +++ b/elpa/smartparens-20181028.1005/smartparens-lua.el @@ -1,4 +1,4 @@ -;;; smartparens-lua.el --- Additional configuration for Lua based modes. +;;; smartparens-lua.el --- Additional configuration for Lua based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2014 Matus Goljer @@ -48,6 +48,7 @@ (require 'smartparens) (defun sp-lua-post-keyword-insert (id action _context) + "ID, ACTION, CONTEXT." (cond ((eq action 'insert) (cond @@ -68,15 +69,19 @@ (sp-with-modes '(lua-mode) (sp-local-pair "if" "end" :when '(("SPC")) + :unless '(sp-in-comment-p sp-in-string-p) :post-handlers '(sp-lua-post-keyword-insert)) (sp-local-pair "function" "end" :when '(("SPC")) + :unless '(sp-in-comment-p sp-in-string-p) :post-handlers '(sp-lua-post-keyword-insert)) (sp-local-pair "for" "end" :when '(("SPC")) + :unless '(sp-in-comment-p sp-in-string-p) :post-handlers '(sp-lua-post-keyword-insert)) (sp-local-pair "while" "end" :when '(("SPC")) + :unless '(sp-in-comment-p sp-in-string-p) :post-handlers '(sp-lua-post-keyword-insert)) ) diff --git a/elpa/smartparens-20181028.1005/smartparens-markdown.el b/elpa/smartparens-20181028.1005/smartparens-markdown.el new file mode 100644 index 00000000..b097fb5d --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-markdown.el @@ -0,0 +1,88 @@ +;;; smartparens-markdown.el --- Additional configuration for Markdown based modes. -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Matus Goljer + +;; Author: Matus Goljer +;; Maintainer: Matus Goljer +;; Created: 11th May 2017 +;; Keywords: abbrev convenience editing +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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 of the License, or +;; (at your option) any later version. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for Markdown based +;; modes. To use it, simply add: +;; +;; (require 'smartparens-markdown) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. +;; +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) +(require 'smartparens-text) +(eval-when-compile + (defvar markdown-gfm-use-electric-backquote)) + + +(defun sp-gfm-electric-backquote-p (_id action _context) + "Do not insert ```...``` pair if that would be handled by `markdown-electric-backquote'." + (and (eq action 'insert) + markdown-gfm-use-electric-backquote + (sp--looking-back-p "^```"))) + +(defun sp--gfm-point-after-word-p (id action _context) + "Return t if point is after a word, nil otherwise. +This predicate is only tested on \"insert\" action." + (when (eq action 'insert) + (sp--looking-back-p (concat "\\(\\sw\\)" (regexp-quote id))))) + +(defun sp--gfm-skip-asterisk (_ms mb _me) + "Non-nil if we should ignore this asterisk as a delimiter." + (save-excursion + (goto-char mb) + (save-match-data (looking-at "^\\* ")))) + +(sp-with-modes '(markdown-mode gfm-mode) + (sp-local-pair "*" "*" + :unless '(sp--gfm-point-after-word-p sp-point-at-bol-p) + :post-handlers '(("[d1]" "SPC")) + :skip-match 'sp--gfm-skip-asterisk) + (sp-local-pair "**" "**") + (sp-local-pair "_" "_" :unless '(sp-point-after-word-p))) + +(sp-with-modes 'markdown-mode + (sp-local-pair "```" "```")) + +(sp-with-modes 'gfm-mode + (sp-local-pair "`" "`" :unless '(:add sp-gfm-electric-backquote-p)) + (sp-local-pair "```" "```" :unless '(:add sp-gfm-electric-backquote-p))) + +(provide 'smartparens-markdown) +;;; smartparens-markdown.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-ml.el b/elpa/smartparens-20181028.1005/smartparens-ml.el new file mode 100644 index 00000000..55008c4e --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-ml.el @@ -0,0 +1,73 @@ +;;; smartparens-ml.el --- Additional configuration for ML languages -*- lexical-binding: t; -*- + +;; Copyright (C) 2016-2017 Ta Quang Trung +;; Copyright (C) 2017 Matus Goljer + +;; Author: Ta Quang Trung +;; Matus Goljer +;; Louis Roché +;; Maintainer: Matus Goljer +;; Created: 14 July 2016 +;; Keywords: smartparens, ML, ocaml, reason +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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 of the License, or +;; (at your option) any later version. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for ML languages. +;; To use it, simply add: +;; +;; (require 'smartparens-ml) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. +;; +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) + +;;; Local pairs for ML-family languages + +(sp-with-modes '(fsharp-mode) + (sp-local-pair "(*" "*)" )) + +(sp-with-modes '(tuareg-mode) + ;; Disable ` because it is used in polymorphic variants + (sp-local-pair "`" nil :actions nil) + ;; Disable ' because it is used in value names and types + (sp-local-pair "'" nil :actions nil) + (sp-local-pair "(*" "*)" )) + +(sp-with-modes '(reason-mode) + ;; Disable ` because it is used in polymorphic variants + (sp-local-pair "`" nil :actions nil) + ;; Disable ' because it is used in value names and types + (sp-local-pair "'" nil :actions nil) + (sp-local-pair "/*" "*/" )) + +(provide 'smartparens-ml) +;;; smartparens-ml.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-org.el b/elpa/smartparens-20181028.1005/smartparens-org.el new file mode 100644 index 00000000..083c258d --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-org.el @@ -0,0 +1,66 @@ +;;; smartparens-org.el --- Configuration for Org mode. -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Matúš Goljer + +;; Author: Matúš Goljer +;; Maintainer: Matúš Goljer +;; Version: 0.0.1 +;; Created: 15th January 2017 +;; Keywords: languages + +;; 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 +;; of the License, 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 this program. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for Org based +;; modes. To use it, simply add: +;; +;; (require 'smartparens-org) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. +;; +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. +;; +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) +(require 'smartparens-text) + +(defun sp--org-skip-asterisk (_ms mb me) + "Non-nil if the asterisk is part of the outline marker." + (save-excursion + (goto-char mb) + (beginning-of-line) + (let ((skip-distance (skip-chars-forward "*"))) + (if (= skip-distance 1) + (not (memq (syntax-class (syntax-after (point))) '(2 3))) + (<= me (point)))))) + +(sp-with-modes 'org-mode + (sp-local-pair "*" "*" + :unless '(sp-point-after-word-p sp-point-at-bol-p) + :skip-match 'sp--org-skip-asterisk) + (sp-local-pair "_" "_" :unless '(sp-point-after-word-p)) + (sp-local-pair "/" "/" :unless '(sp-point-after-word-p) :post-handlers '(("[d1]" "SPC"))) + (sp-local-pair "~" "~" :unless '(sp-point-after-word-p) :post-handlers '(("[d1]" "SPC"))) + (sp-local-pair "=" "=" :unless '(sp-point-after-word-p) :post-handlers '(("[d1]" "SPC"))) + (sp-local-pair "«" "»")) + +(provide 'smartparens-org) +;;; smartparens-org.el ends here diff --git a/elpa/smartparens-20160217.1102/smartparens-pkg.el b/elpa/smartparens-20181028.1005/smartparens-pkg.el similarity index 52% rename from elpa/smartparens-20160217.1102/smartparens-pkg.el rename to elpa/smartparens-20181028.1005/smartparens-pkg.el index a93a53b4..ee4a1e39 100644 --- a/elpa/smartparens-20160217.1102/smartparens-pkg.el +++ b/elpa/smartparens-20181028.1005/smartparens-pkg.el @@ -1,6 +1,6 @@ -(define-package "smartparens" "20160217.1102" "Automatic insertion, wrapping and paredit-like navigation with user defined pairs." - '((cl-lib "0.3") - (dash "2.10.0"))) +(define-package "smartparens" "20181028.1005" "Automatic insertion, wrapping and paredit-like navigation with user defined pairs." + '((dash "2.13.0") + (cl-lib "0.3"))) ;; Local Variables: ;; no-byte-compile: t ;; End: diff --git a/elpa/smartparens-20160217.1102/smartparens-python.el b/elpa/smartparens-20181028.1005/smartparens-python.el similarity index 65% rename from elpa/smartparens-20160217.1102/smartparens-python.el rename to elpa/smartparens-20181028.1005/smartparens-python.el index 2cee7159..d6ac93ff 100644 --- a/elpa/smartparens-20160217.1102/smartparens-python.el +++ b/elpa/smartparens-20181028.1005/smartparens-python.el @@ -1,4 +1,4 @@ -;;; smartparens-python.el --- Additional configuration for Python based modes. +;;; smartparens-python.el --- Additional configuration for Python based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2016 Matus Goljer @@ -53,40 +53,34 @@ (--each '(python-mode inferior-python-mode) (add-to-list 'sp-sexp-suffix (list it 'regexp ""))) -(defun sp-python-in-string-p (id action context) - "Return non-nil if point is in a string, taking -care not to be confused by an unclosed ' that's just been typed." - (and (eq context 'string) - (save-excursion - (backward-char 1) - (nth 3 (syntax-ppss))))) - -(sp-local-pair 'python-mode - "'" "'" - :unless '(sp-in-comment-p sp-python-in-string-p)) - -(sp-local-pair 'python-mode - "(" nil - :pre-handlers '(sp-python-pre-slurp-handler)) - -(sp-local-pair 'python-mode - "[" nil - :pre-handlers '(sp-python-pre-slurp-handler)) - -(defun sp-python-pre-slurp-handler (id action context) - (when (eq action 'slurp-forward) - ;; If there was no space before, we shouldn't add on. - ;; ok = enclosing, next-thing one being slurped into - ;; (variables let-bound in `sp-forward-slurp-sexp'). - (save-excursion - (when (and (= (sp-get ok :end) (sp-get next-thing :beg)) - (equal (sp-get ok :op) (sp-get next-thing :op))) - (goto-char (sp-get ok :end)) - (when (looking-back " ") - (delete-char -1)))))) +(sp-with-modes 'python-mode + (sp-local-pair "'" "'" :unless '(sp-in-comment-p sp-in-string-quotes-p) :post-handlers '(:add sp-python-fix-tripple-quotes)) + (sp-local-pair "\"" "\"" :post-handlers '(:add sp-python-fix-tripple-quotes)) + (sp-local-pair "'''" "'''") + (sp-local-pair "\\'" "\\'") + (sp-local-pair "\"\"\"" "\"\"\"")) + +(defun sp-python-fix-tripple-quotes (id action _context) + "Properly rewrap tripple quote pairs. + +When the user rewraps a tripple quote pair to the other pair +type (i.e. ''' to \") we check if the old pair was a +tripple-quote pair and if so add two pairs to beg/end of the +newly formed pair (which was a single-quote \"...\" pair)." + (when (eq action 'rewrap-sexp) + (let ((old (plist-get sp-handler-context :parent))) + (when (or (and (equal old "'''") (equal id "\"")) + (and (equal old "\"\"\"") (equal id "'"))) + (save-excursion + (sp-get sp-last-wrapped-region + (goto-char :end-in) + (insert (make-string 2 (aref id 0))) + (goto-char :beg) + (insert (make-string 2 (aref id 0))))))))) (defadvice python-indent-dedent-line-backspace (around sp-backward-delete-char-advice activate) + "Fix indend." (if smartparens-strict-mode (cl-letf (((symbol-function 'delete-backward-char) (lambda (arg &optional killp) diff --git a/elpa/smartparens-20160217.1102/smartparens-racket.el b/elpa/smartparens-20181028.1005/smartparens-racket.el similarity index 95% rename from elpa/smartparens-20160217.1102/smartparens-racket.el rename to elpa/smartparens-20181028.1005/smartparens-racket.el index 06a6551b..08af6760 100644 --- a/elpa/smartparens-20160217.1102/smartparens-racket.el +++ b/elpa/smartparens-20181028.1005/smartparens-racket.el @@ -1,4 +1,4 @@ -;;; smartparens-racket.el --- Additional configuration for Racket based modes. +;;; smartparens-racket.el --- Additional configuration for Racket based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2015 Vikraman Choudhury @@ -49,6 +49,7 @@ (sp-with-modes '(racket-mode racket-repl-mode) (sp-local-pair "`" nil :actions nil) + (sp-local-pair "'" nil :actions nil) (sp-local-pair "#|" "|#")) (provide 'smartparens-racket) diff --git a/elpa/smartparens-20160217.1102/smartparens-ruby.el b/elpa/smartparens-20181028.1005/smartparens-ruby.el similarity index 63% rename from elpa/smartparens-20160217.1102/smartparens-ruby.el rename to elpa/smartparens-20181028.1005/smartparens-ruby.el index efb0c6ab..ed504842 100644 --- a/elpa/smartparens-20160217.1102/smartparens-ruby.el +++ b/elpa/smartparens-20181028.1005/smartparens-ruby.el @@ -1,4 +1,4 @@ -;;; smartparens-ruby.el --- Additional configuration for Ruby based modes. +;;; smartparens-ruby.el --- Additional configuration for Ruby based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2014 Jean-Louis Giordano @@ -48,25 +48,35 @@ (require 'smartparens) +(declare-function enh-ruby-forward-sexp "ruby") +(declare-function ruby-forward-sexp "ruby") +(declare-function enh-ruby-backward-sexp "ruby") +(declare-function ruby-backward-sexp "ruby") + (defun sp-ruby-forward-sexp () + "Wrapper for `ruby-forward-sexp' based on `enh-ruby-mode'." (interactive) (if (boundp 'enh-ruby-forward-sexp) (enh-ruby-forward-sexp) (ruby-forward-sexp))) (defun sp-ruby-backward-sexp () + "Wrapper for `ruby-backward-sexp' based on `enh-ruby-mode'." (interactive) (if (boundp 'enh-ruby-backward-sexp) (enh-ruby-backward-sexp) (ruby-backward-sexp))) (defun sp-ruby-maybe-one-space () - (while (looking-back " ") (backward-char)) + "Turn whitespace around point to just one space." + (while (looking-back " " nil) (backward-char)) (when (or (looking-at-p " ") (looking-at-p "}") - (looking-back "{")) + (looking-back "{" nil) + (and (looking-at-p "\\sw") + (looking-back ":" nil))) (save-excursion (just-one-space))) - (when (and (not (looking-back "^.?")) + (when (and (not (looking-back "^.?" nil)) (save-excursion (backward-char 2) (or (looking-at-p ".[^:] [.([,;]") @@ -76,12 +86,15 @@ (delete-char 1))) (defun sp-ruby-delete-indentation (&optional arg) - "Better way of joining ruby lines" + "Better way of joining ruby lines. + +ARG is how many indentation to delete." (delete-indentation arg) (sp-ruby-maybe-one-space)) (defun sp-ruby-block-post-handler (id action context) - "Handler for ruby block-like inserts" + "Handler for ruby block-like insertions. +ID, ACTION, CONTEXT." (when (equal action 'insert) (save-excursion (newline) @@ -90,7 +103,8 @@ (sp-ruby-post-handler id action context)) (defun sp-ruby-def-post-handler (id action context) - "Handler for ruby def-like inserts" + "Handler for ruby def-like insertions. +ID, ACTION, CONTEXT." (when (equal action 'insert) (save-excursion (insert "x") @@ -99,139 +113,163 @@ (delete-char 1)) (sp-ruby-post-handler id action context)) -(defun sp-ruby-post-handler (id action context) - (when (equal action 'barf-backward) - (sp-ruby-delete-indentation 1) - (indent-according-to-mode) - (save-excursion - (sp-backward-sexp) ; move to begining of current sexp - (sp-backward-sexp arg) - (sp-ruby-maybe-one-space))) +(defun sp-ruby-post-handler (_id action _context) + "Ruby post handler. +ID, ACTION, CONTEXT." + (-let (((&plist :arg arg :enc enc) sp-handler-context)) + (when (equal action 'barf-backward) + (sp-ruby-delete-indentation 1) + (indent-according-to-mode) + (save-excursion + (sp-backward-sexp) ; move to begining of current sexp + (sp-backward-sexp arg) + (sp-ruby-maybe-one-space))) + + (when (equal action 'barf-forward) + (sp-get enc + (let ((beg-line (line-number-at-pos :beg-in)) + (end-line (line-number-at-pos :end-in))) + (sp-forward-sexp arg) + (sp-ruby-maybe-one-space) + (when (not (= (line-number-at-pos) beg-line)) + (sp-ruby-delete-indentation -1)) + (indent-according-to-mode)))))) - (when (equal action 'barf-forward) +(defun sp-ruby-pre-handler (_id action _context) + "Handler for ruby slurp and barf. +ID, ACTION, CONTEXT." + (let ((enc (plist-get sp-handler-context :enc))) (sp-get enc (let ((beg-line (line-number-at-pos :beg-in)) (end-line (line-number-at-pos :end-in))) - (sp-forward-sexp arg) - (sp-ruby-maybe-one-space) - (when (not (= (line-number-at-pos) beg-line)) - (sp-ruby-delete-indentation -1)) - (indent-according-to-mode))))) - -(defun sp-ruby-pre-handler (id action context) - "Handler for ruby slurp and barf" - (sp-get enc - (let ((beg-line (line-number-at-pos :beg-in)) - (end-line (line-number-at-pos :end-in))) - - (when (equal action 'slurp-backward) - (save-excursion - (sp-forward-sexp) - (when (looking-at-p ";") (forward-char)) - (sp-ruby-maybe-one-space) - (when (not (= (line-number-at-pos) end-line)) - (sp-ruby-delete-indentation -1))) - (while (thing-at-point-looking-at "\\.[ \n]*") - (sp-backward-sexp)) - (when (looking-back "[@$:&?!]") - (backward-char) - (when (looking-back "[@&:]") - (backward-char))) - (just-one-space) - (save-excursion + + (when (equal action 'slurp-backward) + (save-excursion + (sp-forward-sexp) + (when (looking-at-p ";") (forward-char)) + (sp-ruby-maybe-one-space) + (when (not (= (line-number-at-pos) end-line)) + (sp-ruby-delete-indentation -1))) + (when (looking-at-p "::") + (while (and (looking-back "\\sw" nil) + (--when-let (sp-get-symbol t) + (sp-get it (goto-char :beg-prf)))))) + (while (thing-at-point-looking-at "\\.[[:blank:]\n]*") + (sp-backward-sexp)) + (when (looking-back "[@$:&?!]" nil) + (backward-char) + (when (looking-back "[@&:]" nil) + (backward-char))) + (just-one-space) + (save-excursion + (if (= (line-number-at-pos) end-line) + (insert " ") + (newline)))) + + (when (equal action 'barf-backward) + ;; Barf whole method chains + (while (thing-at-point-looking-at "[(.:[][\n[:blank:]]*") + (sp-forward-sexp)) + (if (looking-at-p " *$") + (newline) + (save-excursion (newline)))) + + (when (equal action 'slurp-forward) + (save-excursion + (sp-backward-sexp) + (when (looking-back "\." nil) (backward-char)) + (sp-ruby-maybe-one-space) + (when (not (= (line-number-at-pos) beg-line)) + (if (thing-at-point-looking-at "\\.[[:blank:]\n]*") + (progn + (forward-symbol -1) + (sp-ruby-delete-indentation -1)) + (sp-ruby-delete-indentation)))) + (while (looking-at-p "::") (sp-forward-symbol)) + (when (looking-at-p "[?!;]") (forward-char)) + (if (= (line-number-at-pos) beg-line) + (insert " ") + (newline))) + + (when (equal action 'barf-forward) + (when (looking-back "\\." nil) (backward-char)) + (when (looking-at-p "::") + (while (and (looking-back "\\sw" nil) + (--when-let (sp-get-symbol t) + (sp-get it (goto-char :beg-prf)))))) (if (= (line-number-at-pos) end-line) (insert " ") - (newline)))) - - (when (equal action 'barf-backward) - ;; Barf whole method chains - (while (thing-at-point-looking-at "[.([:][ \n]*") - (sp-forward-sexp)) - (if (looking-at-p " *$") - (newline) - (save-excursion (newline)))) - - (when (equal action 'slurp-forward) - (save-excursion - (sp-backward-sexp) - (when (looking-back "\.") (backward-char)) - (sp-ruby-maybe-one-space) - (when (not (= (line-number-at-pos) beg-line)) - (if (thing-at-point-looking-at "\\.[ \n]*") - (progn - (forward-symbol -1) - (sp-ruby-delete-indentation -1)) - (sp-ruby-delete-indentation)))) - (while (looking-at-p "::") (sp-forward-symbol)) - (when (looking-at-p "[?!;]") (forward-char)) - (if (= (line-number-at-pos) beg-line) - (insert " ") - (newline))) - - (when (equal action 'barf-forward) - (when (looking-back "\\.") (backward-char)) - (while (looking-back "::") (sp-backward-symbol)) - (if (= (line-number-at-pos) end-line) - (insert " ") - (if (looking-back "^ *") - (save-excursion (newline)) - (newline))))))) + (if (looking-back "^[[:blank:]]*" nil) + (save-excursion (newline)) + (newline)))))))) (defun sp-ruby-inline-p (id) + "Test if ID is inline." (save-excursion - (when (looking-back id) + (when (looking-back id nil) (backward-word)) - (when (not (or (looking-back "^ *") - (looking-back "= *"))) + (when (not (or (looking-back "^[[:blank:]]*" nil) + (looking-back "= *" nil))) (or (save-excursion - (forward-symbol -1) - (forward-symbol 1) - (looking-at-p (concat " *" id))) + (forward-symbol -1) + (forward-symbol 1) + (looking-at-p (concat " *" id))) (save-excursion ;; This does not seem to make emacs snapshot happy (ignore-errors (sp-ruby-backward-sexp) (sp-ruby-forward-sexp) - (looking-at-p (concat "[^ ]* *" id)))))))) + (looking-at-p (concat "[^[:blank:]]* *" id)))))))) (defun sp-ruby-method-p (id) + "Test if ID is a method." (save-excursion - (when (looking-back id) + (when (looking-back id nil) (backward-word)) (and (looking-at-p id) (or ;; fix for def_foo (looking-at-p (concat id "[_?!:]")) ;; fix for foo_def - (looking-back "[_:@$.]") + (looking-back "[_:@$.]" nil) ;; fix for def for; end - (looking-back "def \\|class \\|module ") + (looking-back "def \\|class \\|module " nil) ;; Check if multiline method call ;; But beware of comments! - (and (looking-back "\\.[ \n]*") + (and (looking-back "\\.[[:blank:]\n]*" nil) (not (save-excursion (search-backward ".") (sp-point-in-comment)))))))) -(defun sp-ruby-skip-inline-match-p (ms mb me) +(defun sp-ruby-skip-inline-match-p (ms _mb _me) + "If non-nil, skip inline match. +MS, MB, ME." (or (sp-ruby-method-p ms) (sp-ruby-inline-p ms))) -(defun sp-ruby-skip-method-p (ms mb me) +(defun sp-ruby-skip-method-p (ms _mb _me) + "If non-nil, skip method. +MS, MB, ME." (sp-ruby-method-p ms)) (defun sp-ruby-in-string-or-word-p (id action context) + "Test if point is inside string or word. +ID, ACTION, CONTEXT." (or (sp-in-string-p id action context) - (and (looking-back id) - (not (looking-back (sp--strict-regexp-quote id)))) + (and (looking-back id nil) + (not (looking-back (sp--strict-regexp-quote id) nil))) (sp-ruby-method-p id))) (defun sp-ruby-in-string-word-or-inline-p (id action context) + "Test if point is inside string, word or inline. +ID, ACTION, CONTEXT." (or (sp-ruby-in-string-or-word-p id action context) - (and (looking-back id) + (and (looking-back id nil) (sp-ruby-inline-p id)))) -(defun sp-ruby-pre-pipe-handler (id action context) +(defun sp-ruby-pre-pipe-handler (id action _context) + "Ruby pipe handler. +ID, ACTION, CONTEXT." (when (equal action 'insert) (save-excursion (just-one-space)) @@ -239,24 +277,28 @@ (search-backward id) (just-one-space)))) -(defun sp-ruby-should-insert-pipe-close (id action _ctx) - "Test whether to insert the closing pipe for a lambda-binding pipe pair." +(defun sp-ruby-should-insert-pipe-close (id action _context) + "Test whether to insert the closing pipe for a lambda-binding pipe pair. +ID, ACTION, CONTEXT" (if (eq action 'insert) (thing-at-point-looking-at (rx-to-string `(and (or "do" "{") (* space) ,id))) t)) (defun sp--ruby-skip-match (ms me mb) + "Ruby skip match. +MS, ME, MB." (when (string= ms "end") (or (sp-in-string-p ms me mb) (sp-ruby-method-p "end")))) (add-to-list 'sp-navigate-skip-match - '((ruby-mode enh-ruby-mode) . sp--ruby-skip-match)) + '((ruby-mode enh-ruby-mode motion-mode) . sp--ruby-skip-match)) -(add-to-list 'sp-sexp-suffix '(ruby-mode syntax "")) +(dolist (mode '(ruby-mode motion-mode)) + (add-to-list 'sp-sexp-suffix `(,mode syntax ""))) -(sp-with-modes '(ruby-mode enh-ruby-mode) +(sp-with-modes '(ruby-mode enh-ruby-mode motion-mode) (sp-local-pair "do" "end" :when '(("SPC" "RET" "")) :unless '(sp-ruby-in-string-or-word-p sp-in-comment-p) @@ -365,8 +407,6 @@ :pre-handlers '(sp-ruby-pre-pipe-handler) :suffix "")) -(add-to-list 'sp-navigate-consider-stringlike-sexp 'ruby-mode) - (provide 'smartparens-ruby) ;;; smartparens-ruby.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-rust.el b/elpa/smartparens-20181028.1005/smartparens-rust.el new file mode 100644 index 00000000..17d29b6e --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-rust.el @@ -0,0 +1,140 @@ +;;; smartparens-rust.el --- Additional configuration for Rust based modes. -*- lexical-binding: t; -*- + +;; Copyright (C) 2015 Wilfred Hughes + +;; Created: 3 November 2015 +;; Keywords: abbrev convenience editing +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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 of the License, or +;; (at your option) any later version. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for Rust. To use +;; it, simply add: +;; +;; (require 'smartparens-config) +;; +;; alternatively, you can explicitly load these preferences: +;; +;; (require 'smartparens-rust) +;; +;; in your configuration. + +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: +(require 'smartparens) + +(declare-function rust-mode "rust-mode") + +(defun sp-in-rust-lifetime-context (&rest _args) + "Return t if point is in a Rust context where ' represents a lifetime. +If we return nil, ' should be used for character literals. +ARGS." + (or + (condition-case nil + ;; If point is just after a &', it's probably a &'foo. + (save-excursion + (backward-char 2) + (looking-at "&")) + ;; If we're at the beginning of the buffer, just carry on. + (beginning-of-buffer)) + ;; If point is inside < > it's probably a parameterised function. + (let ((paren-pos (nth 1 (syntax-ppss)))) + (and paren-pos + (save-excursion + (goto-char paren-pos) + (looking-at "<")))))) + +(defun sp-rust-skip-match-angle-bracket (_ms _mb me) + "Non-nil if we should ignore the bracket as valid delimiter." + (save-excursion + (goto-char me) + (let ((on-fn-return-type + (sp--looking-back-p (rx "->") nil)) + (on-match-branch + (sp--looking-back-p (rx "=>") nil)) + (on-comparison + (sp--looking-back-p (rx (or + (seq space "<") + (seq space ">") + (seq space "<<") + (seq space ">>"))) + nil))) + (or on-comparison on-fn-return-type on-match-branch)))) + +(defun sp-rust-filter-angle-brackets (_id action context) + "Non-nil if we should allow ID's ACTION in CONTEXT for angle brackets." + ;; See the docstring for `sp-pair' for the possible values of ID, + ;; ACTION and CONTEXT. + (cond + ;; Inside strings, don't do anything with < or >. + ((eq context 'string) + nil) + ;; Don't do any smart pairing inside comments either. + ((eq context 'comment) + nil) + ;; Otherwise, we're in code. + ((eq context 'code) + (let ((on-fn-return-type + (looking-back (rx "->") nil)) + (on-match-branch + (looking-back (rx "=>") nil)) + (on-comparison + (looking-back (rx (or + (seq space "<") + (seq space ">") + (seq space "<<") + (seq space ">>"))) + nil))) + (cond + ;; Only insert a matching > if we're not looking at a + ;; comparison. + ((eq action 'insert) + (and (not on-comparison) (not on-fn-return-type) (not on-match-branch))) + ;; Always allow wrapping in a pair if the region is active. + ((eq action 'wrap) + (not on-match-branch)) + ;; When pressing >, autoskip if we're not looking at a + ;; comparison. + ((eq action 'autoskip) + (and (not on-comparison) (not on-fn-return-type) (not on-match-branch))) + ;; Allow navigation, highlighting and strictness checks if it's + ;; not a comparison. + ((eq action 'navigate) + (and (not on-comparison) (not on-fn-return-type) (not on-match-branch)))))))) + +(sp-with-modes '(rust-mode) + (sp-local-pair "'" "'" + :unless '(sp-in-comment-p sp-in-string-quotes-p sp-in-rust-lifetime-context) + :post-handlers'(:rem sp-escape-quotes-after-insert)) + (sp-local-pair "<" ">" + :when '(sp-rust-filter-angle-brackets) + :skip-match 'sp-rust-skip-match-angle-bracket)) + +;; Rust has no sexp suffices. This fixes slurping +;; (|foo).bar -> (foo.bar) +(add-to-list 'sp-sexp-suffix (list #'rust-mode 'regexp "")) + +(provide 'smartparens-rust) + +;;; smartparens-rust.el ends here diff --git a/elpa/smartparens-20160217.1102/smartparens-scala.el b/elpa/smartparens-20181028.1005/smartparens-scala.el similarity index 69% rename from elpa/smartparens-20160217.1102/smartparens-scala.el rename to elpa/smartparens-20181028.1005/smartparens-scala.el index 29e9df0d..7e79016d 100644 --- a/elpa/smartparens-20160217.1102/smartparens-scala.el +++ b/elpa/smartparens-20181028.1005/smartparens-scala.el @@ -1,4 +1,4 @@ -;;; smartparens-scala.el --- Additional configuration for Scala based modes. +;;; smartparens-scala.el --- Additional configuration for Scala based modes. -*- lexical-binding: t; -*- ;; Copyright (C) 2015 Greg Nwosu @@ -53,5 +53,28 @@ (--each '(scala-mode inferior-scala-mode) (add-to-list 'sp-sexp-suffix (list it 'regexp ""))) +(defun sp-scala-wrap-with-indented-newlines (_1 action _2) + "Post handler for the wrap ACTION, putting the region on indented newlines." + (when (eq action 'wrap) + (sp-get sp-last-wrapped-region + (let ((beg :beg-in) + (end :end-in)) + (save-excursion + (goto-char end) + (newline-and-indent)) + (save-excursion + (goto-char beg) + (newline)) + (indent-region beg end))))) + +(sp-local-pair 'scala-mode "(" nil + :post-handlers '(("||\n[i]" "RET") + ("| " "SPC"))) + +(sp-local-pair 'scala-mode "{" nil + :post-handlers '(("||\n[i]" "RET") + ("| " "SPC") + sp-scala-wrap-with-indented-newlines)) + (provide 'smartparens-scala) ;;; smartparens-scala.el ends here diff --git a/elpa/smartparens-20181028.1005/smartparens-text.el b/elpa/smartparens-20181028.1005/smartparens-text.el new file mode 100644 index 00000000..91c07be1 --- /dev/null +++ b/elpa/smartparens-20181028.1005/smartparens-text.el @@ -0,0 +1,65 @@ +;;; smartparens-latex.el --- Additional configuration for text-mode. -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Matus Goljer + +;; Author: Matus Goljer +;; Maintainer: Matus Goljer +;; Created: 16 July 2017 +;; Keywords: abbrev convenience editing +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens 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 of the License, or +;; (at your option) any later version. + +;; Smartparens 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 Smartparens. If not, see . + +;;; Commentary: + +;; This file provides some additional configuration for `text-mode'. +;; To use it, simply add: +;; +;; (require 'smartparens-text) +;; +;; into your configuration. You can use this in conjunction with the +;; default config or your own configuration. + +;; If you have good ideas about what should be added please file an +;; issue on the github tracker. + +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: + +(require 'smartparens) + +(defun sp-text-mode-emoticon-p (_id action _context) + (when (memq action '(insert navigate)) + (sp--looking-back-p ":-?[()]" 3))) + +(defun sp-text-mode-skip-emoticon (ms mb _me) + (when (member ms '("(" ")")) + (save-excursion + (goto-char mb) + (sp--looking-back-p ":-?" 2)))) + +(sp-local-pair 'text-mode "(" nil + :unless '(:add sp-text-mode-emoticon-p) + :skip-match 'sp-text-mode-skip-emoticon) + +(provide 'smartparens-text) +;;; smartparens-text.el ends here diff --git a/elpa/smartparens-20160217.1102/smartparens.el b/elpa/smartparens-20181028.1005/smartparens.el similarity index 62% rename from elpa/smartparens-20160217.1102/smartparens.el rename to elpa/smartparens-20181028.1005/smartparens.el index a9ed420d..c59f31c1 100644 --- a/elpa/smartparens-20160217.1102/smartparens.el +++ b/elpa/smartparens-20181028.1005/smartparens.el @@ -1,4 +1,4 @@ -;;; smartparens.el --- Automatic insertion, wrapping and paredit-like navigation with user defined pairs. +;;; smartparens.el --- Automatic insertion, wrapping and paredit-like navigation with user defined pairs. -*- lexical-binding: t -*- ;; Copyright (C) 2012-2016 Matus Goljer @@ -49,15 +49,36 @@ ;;; Code: -(eval-when-compile (require 'cl)) ; for `lexical-let' -(require 'cl-lib) +(eval-when-compile + (require 'subr-x) ; for `string-trim' + (require 'cl-lib)) + (require 'dash) (require 'thingatpt) +(require 'help-mode) ;; for help-xref-following #85 + +(declare-function cua-replace-region "cua-base") ; FIXME: remove this when we drop support for old emacs +(declare-function cua-delete-region "cua-base") +(declare-function cua--fallback "cua-base") +(declare-function package-version-join "package") +(declare-function package-desc-version "package") + +(declare-function subword-kill "subword") +(declare-function subword-forward "subword") +(declare-function subword-backward "subword") + +(declare-function hungry-delete-backward "hungry-delete") +(declare-function hungry-delete-forward "hungry-delete") + +(declare-function evil-get-register "evil-common") +(declare-function evil-set-register "evil-common") +(eval-when-compile + (defvar evil-this-register) + (defvar package-alist) + (defvar sp-autoskip-closing-pair) + (defvar sp-show-enclosing-pair-commands) + (defvar show-smartparens-mode)) -(eval-when-compile (defvar cua--region-keymap)) -(declare-function cua-replace-region "cua-base") -(declare-function cua--pre-command-handler "cua-base") -(declare-function delete-selection-pre-hook "delsel") ;;; backport for older emacsen @@ -78,7 +99,7 @@ buffer-local wherever it is set." Without a prefix argument, print only the short documentation and examples. -With non-nil prefix argument, show the full documentation for each function. +With non-nil prefix argument ARG, show the full documentation for each function. You can follow the links to the function or variable help page. To get back to the full list, use \\[help-go-back]. @@ -90,13 +111,11 @@ Examples are fontified using the `font-lock-string-face' for better orientation." (interactive "P") (setq arg (not arg)) - (require 'help-mode) ;; for help-xref-following #85 (let ((do-not-display '( smartparens-mode smartparens-global-mode turn-on-smartparens-mode turn-off-smartparens-mode - sp--cua-replace-region sp-wrap-cancel sp-remove-active-pair-overlay sp-splice-sexp-killing-around ;; is aliased to `sp-raise-sexp' @@ -165,6 +184,43 @@ better orientation." (goto-char (point-min)))) (pop-to-buffer "*Smartparens cheat sheet*"))) +(defun sp-describe-system (starterkit) + "Describe user's system. + +The output of this function can be used in bug reports." + (interactive + (list (completing-read "Starterkit/Distribution used: " + (list + "Spacemacs" + "Evil" + "Vanilla" + )))) + (let ((text (format "- `smartparens` version: %s +- Active `major-mode`: `%s` +- Smartparens strict mode: %s +- Emacs version (`M-x emacs-version`): %s +- Starterkit/Distribution: %s +- OS: %s" + (--if-let (cadr (assoc 'smartparens package-alist)) + (package-version-join (package-desc-version it)) + "") + (symbol-name major-mode) + (bound-and-true-p smartparens-strict-mode) + (replace-regexp-in-string "\n" "" (emacs-version)) + starterkit + (symbol-name system-type)))) + (pop-to-buffer + (with-current-buffer (get-buffer-create "*sp-describe-system*") + (erase-buffer) + (insert "The content of the buffer underneath the line was +copied to your clipboard. You can also edit it in this buffer +and then copy the results manually. +------------------------------------------------ +") + (insert text) + (current-buffer))) + (kill-new text))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Variables @@ -207,6 +263,7 @@ function will be considered." ("M-" . sp-splice-sexp-killing-backward) ("M-" . sp-splice-sexp-killing-forward) ("M-r" . sp-splice-sexp-killing-around) + ("M-(" . sp-wrap-round) ("C-)" . sp-forward-slurp-sexp) ;; barf/slurp ("C-" . sp-forward-slurp-sexp) ("C-}" . sp-forward-barf-sexp) @@ -216,8 +273,12 @@ function will be considered." ("C-{" . sp-backward-barf-sexp) ("C-M-" . sp-backward-barf-sexp) ("M-S" . sp-split-sexp) ;; misc + ("M-j" . sp-join-sexp) + ("M-?" . sp-convolute-sexp) ) - "Alist containing the default paredit bindings to corresponding + "Paredit inspired bindings. + +Alist containing the default paredit bindings to corresponding smartparens functions.") (defun sp--populate-keymap (bindings) @@ -227,9 +288,7 @@ smartparens functions.") ;;;###autoload (defun sp-use-paredit-bindings () - "Initiate `smartparens-mode-map' with paredit-compatible bindings for -corresponding functions provided by smartparens. See variable -`sp-paredit-bindings'." + "Initiate `smartparens-mode-map' with `sp-paredit-bindings'." (interactive) (sp--populate-keymap sp-paredit-bindings)) @@ -258,6 +317,7 @@ corresponding functions provided by smartparens. See variable ("C-S-" . sp-splice-sexp-killing-around) ("C-]" . sp-select-next-thing-exchange) ("C-M-]" . sp-select-next-thing) + ("C-M-SPC" . sp-mark-sexp) ("M-F" . sp-forward-symbol) ("M-B" . sp-backward-symbol) ) @@ -265,36 +325,43 @@ corresponding functions provided by smartparens. See variable ;;;###autoload (defun sp-use-smartparens-bindings () - "Initiate `smartparens-mode-map' with smartparens bindings for navigation functions. -See variable `sp-smartparens-bindings'." + "Initiate `smartparens-mode-map' with `sp-smartparens-bindings'." (interactive) (sp--populate-keymap sp-smartparens-bindings)) (defun sp--set-base-key-bindings (&optional symbol value) "Set up the default keymap based on `sp-base-key-bindings'. +SYMBOL is the symbol being set, that is `sp-base-key-bindings'. + +VALUE is the saved value (as a symbol), can be one of: +- sp +- paredit + This function is also used as a setter for this customize value." (when symbol (set-default symbol value)) (cond - ((eq sp-base-key-bindings 'sp) + ((eq value 'sp) (sp-use-smartparens-bindings)) - ((eq sp-base-key-bindings 'paredit) + ((eq value 'paredit) (sp-use-paredit-bindings)))) (defun sp--update-override-key-bindings (&optional symbol value) "Override the key bindings with values from `sp-override-key-bindings'. +SYMBOL is `sp-override-key-bindings', VALUE is the value being set. + This function is also used as a setter for this customize value." (when symbol (set-default symbol value)) ;; this also needs to reload the base set, if any is present. (sp--set-base-key-bindings) - (sp--populate-keymap sp-override-key-bindings)) + (sp--populate-keymap value)) (defcustom sp-base-key-bindings nil "A default set of key bindings for commands provided by smartparens. Paredit binding adds the bindings in `sp-paredit-bindings' to the -corresponding smartparens commands. It does not add bindings to +corresponding smartparens commands. It does not add bindings to any other commands, or commands that do not have a paredit counterpart. @@ -355,6 +422,35 @@ Maximum length of opening or closing pair is "Symbol holding the last successful operation.") (make-variable-buffer-local 'sp-last-operation) +(cl-defstruct sp-state + "Smartparens state for the current buffer." + ;; A "counter" to track delayed hook. When a pair is inserted, a + ;; cons of the form (:next . pair) is stored. On the next + ;; (immediately after insertion) invocation of post-command-hook, it + ;; is changed to (:this . pair). When the `car' is :this, the + ;; post-command-hook checks the delayed hooks for `pair' and + ;; executes them, then reset the "counter". + delayed-hook + ;; TODO + delayed-insertion + ;; The last point checked by sp--syntax-ppss and its result, used for + ;; memoization + last-syntax-ppss-point ;; a list (point point-min point-max) + last-syntax-ppss-result + ;; Value of `sp-pair-list' for this buffer. Note that this might + ;; differ from `sp-pair-list' which is often changed by dynamic + ;; binding + pair-list + ;; Value of `sp-local-pairs' for this buffer. Note that this might + ;; differ from `sp-local-pairs' which is often changed by dynamic + ;; binding + local-pairs + ) + +(defvar sp-state (make-sp-state) + "Smartparens state for the current buffer.") +(make-variable-buffer-local 'sp-state) + ;; TODO: get rid of this (defvar sp-previous-point -1 "Location of point before last command. @@ -391,9 +487,11 @@ location of point before the wrapping.") (make-variable-buffer-local 'sp-last-inserted-pair) (defvar sp-delayed-pair nil - "A pair whose insertion is delayed to be carried out in -`sp--post-command-hook-handler'. The format is (opening delim -. beg of the opening delim)") + "The pair whose insertion is being delayed. + +The insertion of this pair is delayed to be carried out in +`sp--post-command-hook-handler'. The format is (opening delim +. beg of the opening delim)") (make-variable-buffer-local 'sp-delayed-pair) (defvar sp-last-wrapped-region nil @@ -408,8 +506,7 @@ Used to remember the state from before `self-insert-command' is run.") (defvar sp-buffer-modified-p nil - "Non-nil if buffer was modified before the advice on -`self-insert-command' executed.") + "Non-nil if buffer was modified before `pre-command-hook'.") (defvar sp-pre-command-point nil "Position of `point' before `this-command' gets executed.") @@ -420,18 +517,33 @@ run.") Only the pairs defined by `sp-pair' are considered. Tag pairs can be of any length.") -(defvar sp-pairs '((t - . - ((:open "\\\\(" :close "\\\\)" :actions (insert wrap autoskip navigate)) - (:open "\\{" :close "\\}" :actions (insert wrap autoskip navigate)) - (:open "\\(" :close "\\)" :actions (insert wrap autoskip navigate)) - (:open "\\\"" :close "\\\"" :actions (insert wrap autoskip navigate)) - (:open "\"" :close "\"" :actions (insert wrap autoskip navigate)) - (:open "'" :close "'" :actions (insert wrap autoskip navigate)) - (:open "(" :close ")" :actions (insert wrap autoskip navigate)) - (:open "[" :close "]" :actions (insert wrap autoskip navigate)) - (:open "{" :close "}" :actions (insert wrap autoskip navigate)) - (:open "`" :close "`" :actions (insert wrap autoskip navigate))))) +(defconst sp-max-prefix-length 100 + "Maximum length of a pair prefix. + +Because prefixes for pairs can be specified using regular +expressions, they can potentially be of arbitrary length. This +settings solves the problem where the parser would decide to +backtrack the entire buffer which would lock up Emacs.") + +(defvar sp-pairs + '((t + . + ((:open "\\\\(" :close "\\\\)" :actions (insert wrap autoskip navigate)) + (:open "\\{" :close "\\}" :actions (insert wrap autoskip navigate)) + (:open "\\(" :close "\\)" :actions (insert wrap autoskip navigate)) + (:open "\\\"" :close "\\\"" :actions (insert wrap autoskip navigate)) + (:open "\"" :close "\"" + :actions (insert wrap autoskip navigate escape) + :unless (sp-in-string-quotes-p) + :post-handlers (sp-escape-wrapped-region sp-escape-quotes-after-insert)) + (:open "'" :close "'" + :actions (insert wrap autoskip navigate escape) + :unless (sp-in-string-quotes-p sp-point-after-word-p) + :post-handlers (sp-escape-wrapped-region sp-escape-quotes-after-insert)) + (:open "(" :close ")" :actions (insert wrap autoskip navigate)) + (:open "[" :close "]" :actions (insert wrap autoskip navigate)) + (:open "{" :close "}" :actions (insert wrap autoskip navigate)) + (:open "`" :close "`" :actions (insert wrap autoskip navigate))))) "List of pair definitions. Maximum length of opening or closing pair is @@ -467,6 +579,7 @@ Symbol is defined as a chunk of text recognized by emacs-lisp-mode eshell-mode geiser-repl-mode + gerbil-mode inf-clojure-mode inferior-emacs-lisp-mode inferior-lisp-mode @@ -481,19 +594,41 @@ Symbol is defined as a chunk of text recognized by slime-repl-mode stumpwm-mode ) - "List of Lisp modes." + "List of Lisp-related modes." + :type '(repeat symbol) + :group 'smartparens) + +(defcustom sp-clojure-modes '( + cider-repl-mode + clojure-mode + clojurec-mode + clojurescript-mode + clojurex-mode + inf-clojure-mode + ) + "List of Clojure-related modes." :type '(repeat symbol) :group 'smartparens) (defcustom sp-no-reindent-after-kill-modes '( python-mode coffee-mode - js2-mode + asm-mode + makefile-gmake-mode + haml-mode ) "List of modes that should not reindent after kill." :type '(repeat symbol) :group 'smartparens) +(defcustom sp-no-reindent-after-kill-indent-line-functions + '( + insert-tab + ) + "List of `indent-line-function's that should not reindent after kill." + :type '(repeat symbol) + :group 'smartparens) + (defvar sp--html-modes '( sgml-mode html-mode @@ -503,48 +638,61 @@ Symbol is defined as a chunk of text recognized by web-mode jinja2-mode html-erb-mode + js-jsx-mode + js2-jsx-mode + rjsx-mode ) "List of HTML modes.") (defvar sp-message-alist '((:unmatched-expression - "Search failed. This means there is unmatched expression somewhere or we are at the beginning/end of file." - "Unmatched expression.") + "Search failed: there is an unmatched expression somewhere or we are at the beginning/end of file" + "Unmatched expression") + (:unbalanced-region + "Can not kill the region: the buffer would end up in an unbalanced state after deleting the active region" + "Killing the region would make the buffer unbalanced" + "Unbalanced region") (:delimiter-in-string - "Opening or closing pair is inside a string or comment and matching pair is outside (or vice versa). Ignored.") + "Ignored: opening or closing pair is inside a string or comment and matching pair is outside (or vice versa)") (:no-matching-tag - "Search failed. No matching tag found." - "No matching tag.") + "Search failed: no matching tag found" + "No matching tag") (:invalid-context-prev - "Invalid context: previous h-sexp ends after the next one." - "Invalid context.") + "Invalid context: previous h-sexp ends after the next one" + "Invalid context") (:invalid-context-cur - "Invalid context: current h-sexp starts after the next one." - "Invalid context.") + "Invalid context: current h-sexp starts after the next one" + "Invalid context") (:no-structure-found - "Previous sexp starts after current h-sexp or no structure was found." - "No valid structure found.") + "Previous sexp starts after current h-sexp or no structure was found" + "No valid structure found") (:invalid-structure - "This operation would result in invalid structure. Ignored." - "Ignored because of invalid structure.") + "Ignored: this operation would result in invalid structure" + "Ignored because of invalid structure") (:cant-slurp - "We can't slurp without breaking strictly balanced expression. Ignored." - "Can't slurp without breaking balance.") + "Ignored: we can not slurp without breaking strictly balanced expression" + "Can not slurp without breaking balance") + (:cant-slurp-context + "Ignored: we can not slurp into different context (comment -> code)" + "Can not slurp into different context") + (:cant-insert-closing-delimiter + "We can not insert unbalanced closing delimiter in strict mode" + "Can not insert unbalanced delimiter") (:blank-sexp - "Point is in blank sexp, nothing to barf." - "Point is in blank sexp.") + "Point is in blank sexp, nothing to barf" + "Point is in blank sexp") (:point-not-deep-enough - "Point has to be at least two levels deep to swap the enclosing delimiters." - "Point has to be at least two levels deep." - "Point not deep enough.") + "Point has to be at least two levels deep to swap the enclosing delimiters" + "Point has to be at least two levels deep" + "Point not deep enough") (:different-type - "The expressions to be joined are of different type." - "Expressions are of different type.")) + "The expressions to be joined are of different type" + "Expressions are of different type")) "List of predefined messages to be displayed by `sp-message'. Each element is a list consisting of a keyword and one or more strings, which are chosen based on the `sp-message-width' -variable. If the latter is `t', the first string is chosen as +variable. If the latter is t, the first string is chosen as default, which should be the most verbose option available.") @@ -572,7 +720,9 @@ You can enable pre-set bindings by customizing (if smartparens-mode (progn (sp--init) + (add-hook 'self-insert-uses-region-functions 'sp-wrap--can-wrap-p nil 'local) (run-hooks 'smartparens-enabled-hook)) + (remove-hook 'self-insert-uses-region-functions 'sp-wrap--can-wrap-p 'local) (run-hooks 'smartparens-disabled-hook))) (defvar smartparens-strict-mode-map @@ -585,6 +735,9 @@ You can enable pre-set bindings by customizing (define-key map [remap kill-word] 'sp-kill-word) (define-key map [remap kill-line] 'sp-kill-hybrid-sexp) (define-key map [remap backward-kill-word] 'sp-backward-kill-word) + (define-key map [remap kill-region] 'sp-kill-region) + (define-key map [remap delete-region] 'sp-delete-region) + (define-key map [remap kill-whole-line] 'sp-kill-whole-line) map) "Keymap used for `smartparens-strict-mode'.") @@ -611,9 +764,15 @@ after the smartparens indicator in the mode list." (unless (-find-indices (lambda (it) (eq (car it) 'smartparens-strict-mode)) minor-mode-overriding-map-alist) (setq minor-mode-overriding-map-alist (cons `(smartparens-strict-mode . ,smartparens-strict-mode-map) minor-mode-overriding-map-alist))) + (put 'sp-backward-delete-char 'delete-selection 'sp--delete-selection-supersede-p) + (put 'sp-delete-char 'delete-selection 'sp--delete-selection-supersede-p) + (add-hook 'self-insert-uses-region-functions 'sp--self-insert-uses-region-strict-p nil 'local) (setq sp-autoskip-closing-pair 'always)) (setq minor-mode-overriding-map-alist (-remove (lambda (it) (eq (car it) 'smartparens-strict-mode)) minor-mode-overriding-map-alist)) + (put 'sp-backward-delete-char 'delete-selection 'supersede) + (put 'sp-delete-char 'delete-selection 'supersede) + (remove-hook 'self-insert-uses-region-functions 'sp--self-insert-uses-region-strict-p 'local) (let ((std-val (car (plist-get (symbol-plist 'sp-autoskip-closing-pair) 'standard-value))) (saved-val (car (plist-get (symbol-plist 'sp-autoskip-closing-pair) 'saved-value)))) (setq sp-autoskip-closing-pair (eval (or saved-val std-val)))))) @@ -624,6 +783,13 @@ after the smartparens indicator in the mode list." turn-on-smartparens-strict-mode :group 'smartparens) +(defcustom sp-ignore-modes-list '( + minibuffer-inactive-mode + ) + "Modes where smartparens mode is inactive if allowed globally." + :type '(repeat symbol) + :group 'smartparens) + ;;;###autoload (defun turn-on-smartparens-strict-mode () "Turn on `smartparens-strict-mode'." @@ -633,9 +799,18 @@ after the smartparens indicator in the mode list." (eq (get major-mode 'mode-class) 'special))) (smartparens-strict-mode 1))) +;;;###autoload +(defun turn-off-smartparens-strict-mode () + "Turn off `smartparens-strict-mode'." + (interactive) + (smartparens-strict-mode -1)) + (defun sp--init () - "Initialize the buffer local pair bindings and other buffer -local variables that depend on the active `major-mode'." + "Initialize the buffer local smartparens state. + + This includes pair bindings and other buffer local variables +that depend on the active `major-mode'." + (setq sp-state (make-sp-state)) ;; setup local pair replacements (sp--update-local-pairs) ;; set the escape char @@ -648,23 +823,76 @@ local variables that depend on the active `major-mode'." (setq sp-comment-char (string char)))))) (defun sp--maybe-init () - "Initialize the buffer if it is not already initialized. See `sp--init'." + "Initialize the buffer if it is not already initialized. + +See `sp--init'." (unless sp-pair-list (sp--init))) +(defun sp--remove-local-pair (open) + "Remove OPEN from `sp-local-pairs'." + (setq sp-local-pairs + (--remove (equal (plist-get it :open) open) + sp-local-pairs))) + +(defun sp--update-sp-pair-list () + "Update `sp-pair-list' according to current value of `sp-local-pairs'." + (setq sp-pair-list + (->> sp-local-pairs + (--map (cons (plist-get it :open) (plist-get it :close))) + (-sort (lambda (x y) (> (length (car x)) (length (car y)))))))) + (defun sp--update-local-pairs () - "Update local pairs after removal or at mode initialization." + "Update local pairs after change or at mode initialization. + +This commands load all the parent major mode definitions and +merges them into current buffer's `sp-local-pairs'." + (let ((parent-modes (-fix (lambda (x) + (--if-let (get (car x) 'derived-mode-parent) + (cons it x) + x)) + (list major-mode)))) + ;; Combine all the definitions from the most ancient parent to the + ;; most recent parent + (--each parent-modes (sp-update-local-pairs it)))) + +(defun sp-update-local-pairs (configuration) + "Update `sp-local-pairs' with CONFIGURATION. + +The pairs are only updated in current buffer not in all buffers +with the same major mode! If you want to update all buffers of +the specific major-modes use `sp-local-pair'. + +CONFIGURATION can be a symbol to be looked up in `sp-pairs' or a +property list corresponding to the arguments of `sp-local-pair' +or a list of such property lists." (setq sp-local-pairs - (->> (sp--merge-with-local major-mode) - (--filter (plist-get it :actions)))) + (cond + ((symbolp configuration) + (sp--merge-pair-configurations (cdr (assq configuration sp-pairs)))) + ((plist-member configuration :open) + (sp--merge-pair-configurations (list configuration))) + (t + (sp--merge-pair-configurations configuration)))) + + ;; Keep only those which have non-nil :actions + (setq sp-local-pairs (--filter (plist-get it :actions) sp-local-pairs)) + ;; update the `sp-pair-list'. This is a list only containing ;; (open.close) cons pairs for easier querying. We also must order ;; it by length of opening delimiter in descending order (first ;; value is the longest) - (setq sp-pair-list - (->> sp-local-pairs - (--map (cons (plist-get it :open) (plist-get it :close))) - (-sort (lambda (x y) (> (length (car x)) (length (car y)))))))) + (sp--update-sp-pair-list) + (setf (sp-state-local-pairs sp-state) sp-local-pairs) + (setf (sp-state-pair-list sp-state) sp-pair-list)) + +(defmacro sp-with-buffers-using-mode (mode &rest body) + "Execute BODY in every existing buffer using `major-mode' MODE." + (declare (indent 1)) + `(--each (buffer-list) + (with-current-buffer it + (when (derived-mode-p ,mode) + ,@body)))) (defun sp--update-local-pairs-everywhere (&rest modes) "Run `sp--update-local-pairs' in all buffers. @@ -677,7 +905,7 @@ MODES." (with-current-buffer it (when (and smartparens-mode (or (not modes) - (memq major-mode modes))) + (--any? (derived-mode-p it) modes))) (sp--update-local-pairs))))) (defcustom smartparens-enabled-hook nil @@ -690,14 +918,6 @@ MODES." :type 'hook :group 'smartparens) -;; global custom -(defcustom sp-ignore-modes-list '( - minibuffer-inactive-mode - ) - "Modes where smartparens mode is inactive if allowed globally." - :type '(repeat symbol) - :group 'smartparens) - ;;;###autoload (define-globalized-minor-mode smartparens-global-mode smartparens-mode @@ -735,8 +955,9 @@ in mode's startup-hook etc.) by calling `smartparens-mode'." :type 'boolean :group 'smartparens) +;; TODO: remove this in 1.12 (defcustom sp-autoinsert-quote-if-followed-by-closing-pair nil - "If non-nil, autoinsert string quote pair even if the point is followed by closing pair. + "If non-nil autoinsert quotes when the point is followed by closing delimiter. This option only changes behaviour of the insertion process if point is inside a string. In other words, if string is not @@ -758,18 +979,21 @@ compatibility. The intended meaning is \"insert the pair if followed by closing pair?\", t = yes." :type 'boolean :group 'smartparens) +(make-obsolete-variable + 'sp-autoinsert-quote-if-followed-by-closing-pair + "the option was removed and no longer has any effect." "1.10") (defcustom sp-autoskip-closing-pair 'always-end - "If t, skip the following closing pair if the expression is + "Determine the behaviour when skipping closing delimiters. + +If t, skip the following closing pair if the expression is active (that is right after insertion). This is controlled by `sp-cancel-autoskip-on-backward-movement'. If set to \"always-end\", skip the closing pair even if the expression is not active and point is at the end of the expression. This only works for expressions with -single-character delimiters. If the expression is a string-like -expression, these must be enabled in current major-mode to work -with this setting, see `sp-navigate-consider-stringlike-sexp'. +single-character delimiters. If set to \"always\", `sp-up-sexp' is called whenever the closing delimiter is typed inside a sexp of the same type. This is the @@ -790,16 +1014,22 @@ See also `sp-skip-closing-pair'." (make-variable-buffer-local 'sp-autoskip-closing-pair) (defcustom sp-autoskip-opening-pair nil - "If non-nil, skip into the following string-like expression + "Determine the behaviour when skipping opening delimiters. + +If non-nil, skip into the following string-like expression instead of inserting a new pair." :type 'boolean :group 'smartparens) (make-variable-buffer-local 'sp-autoskip-opening-pair) +;; TODO: rename to reflect what this actually does (defcustom sp-cancel-autoskip-on-backward-movement t - "If non-nil, autoskip of closing pair is cancelled not only -when point is moved outside of the pair, but also if the point -moved backwards. See `sp-skip-closing-pair' for more info." + "If non-nil, deactivate the active expression on backward movement. + +Note: the name of this variable is a historic coincidence and +will change in some future release to reflect its real purpose. + +See also `sp-skip-closing-pair'." :type 'boolean :group 'smartparens) @@ -857,7 +1087,7 @@ In the description, we consider more than one space :group 'smartparens) (defcustom sp-autodelete-wrap t - "If non-nil, auto delete both opening and closing pair of most recent wrapping. + "If non-nil, autodelete opening and closing pair of most recent wrapping. Deletion command must be the very first command after the insertion, otherwise normal behaviour is applied." @@ -915,31 +1145,32 @@ When non-nil, wrapping with opening pair always jumps to the beginning of the region and wrapping with closing pair always jumps to the end of the region. + |fooM -> [ -> |[foo]M + Mfoo| -> [ -> |[foo]M + |fooM -> ] -> M[foo]| + Mfoo| -> ] -> M[foo]| + When nil, closing pair places the point at the end of the region and the opening pair leaves the point at its original -position (before or after the region)." +position (before or after the region). + + |fooM -> [ -> [|fooM] + Mfoo| -> [ -> M[foo]| + |fooM -> ] -> M[foo]| + Mfoo| -> ] -> M[foo]|" :type 'boolean :group 'smartparens) ;; escaping custom -(defcustom sp-autoescape-string-quote t - "If non-nil, autoescape string quotes if typed inside string." +(defcustom sp-escape-wrapped-region t + "If non-nil, escape special chars inside the just wrapped region." :type 'boolean :group 'smartparens) -(make-obsolete-variable 'sp-autoescape-string-quote "smartparens' global autoescape system was removed." "2015-02-07") - -(defcustom sp-autoescape-string-quote-if-empty '( - python-mode - ) - "List of modes where the string quotes aren't escaped if the string is empty. -You can list modes where multiple quote characters are used for -multi-line strings, such as `python-mode' to make the insertion -less annoying (that is, three times pressing \" would insert -\"\"\"|\"\"\" instead of \"\\\"\\\"|\\\"\\\"\")." - :type '(repeat symbol) +(defcustom sp-escape-quotes-after-insert t + "If non-nil, escape string quotes if typed inside string." + :type 'boolean :group 'smartparens) -(make-obsolete-variable 'sp-autoescape-string-quote-if-empty "smartparens' global autoescape system was removed." "2015-02-07") ;; navigation & manip custom (defcustom sp-navigate-consider-sgml-tags '( @@ -949,22 +1180,8 @@ less annoying (that is, three times pressing \" would insert :type '(repeat symbol) :group 'smartparens) -(defcustom sp-navigate-consider-stringlike-sexp '( - latex-mode - ) - "List of modes where string-like sexps are considered to be sexps. - -A string-like sexp is an expression where opening and closing -delimeter is the same sequence of characters. For example: *...*, -$...$. -Warning: these are problematic in modes where the symbol might -have multiple functions, such as * in markdown, where it denotes -start of list item (unary) OR emphatic text (binary)." - :type '(repeat symbol) - :group 'smartparens) - -(defcustom sp-nagivate-use-textmode-stringlike-parser '((derived . text-mode)) +(defcustom sp-navigate-use-textmode-stringlike-parser '((derived . text-mode)) "List of modes where textmode stringlike parser is used. See `sp-get-textmode-stringlike-expression'. @@ -1003,14 +1220,17 @@ never automatically closed (see `sp-navigate-close-if-unbalanced')." :type 'boolean :group 'smartparens) +;; TODO: add -alist suffix (defcustom sp-navigate-skip-match `( (,sp-lisp-modes . sp--elisp-skip-match) ) - "Alist of list of major-modes and a function used to skip over matches in -`sp-get-paired-expression'. This function takes three arguments: -the currently matched delimiter, beginning of match and end of -match. If this function returns true, the current match will be -skipped. + "Major-mode dependent specifications of skip functions. + +Alist where the key is a list of major-modes and the value is a +function used to skip over matches in `sp-get-paired-expression'. +This function takes three arguments: the currently matched +delimiter, beginning of match and end of match. If this function +returns true, the current match will be skipped. You can use this to skip over expressions that serve multiple functions, such as if/end pair or unary if in Ruby or * in @@ -1027,12 +1247,18 @@ use :skip-match option in `sp-local-pair'." ,@sp-lisp-modes ) ) - "Modes where sexps should be reindented after jumping out of them with `sp-up-sexp'. + "Modes where sexps should be reindented after `sp-up-sexp'. The whitespace between the closing delimiter and last \"thing\" inside the expression is removed. It works analogically for the `sp-backward-up-sexp'. +Note that this also happens when `sp-skip-closing-pair' is +invoked (usually in strict mode when the closing delimiter is +typed) as it calls `sp-up-sexp' internally. This behaviour can +be customized by various settings of `sp-autoskip-closing-pair' +and `sp-autoskip-opening-pair'. + If the mode is in the list \"interactive\", only reindent the sexp if the command was called interactively. This is recommended for general use. @@ -1061,8 +1287,21 @@ point (using `sp-previous-sexp')." :type 'boolean :group 'smartparens) +(defcustom sp-navigate-interactive-always-progress-point nil + "Make point always move in the direction of navigation. + +If non-nil and the function is called interactively, +`sp-next-sexp' and `sp-previous-sexp' will always move the point +to the end/beg of such an expression where the point would end up +being further in the direction of travel. + +Note: this behaviour will become default in release 2.0 and will +cease to be configurable." + :type 'boolean + :group 'smartparens) + (defcustom sp-sexp-prefix nil - "Alist of major-mode specific prefix specification. + "Alist of `major-mode' specific prefix specification. Each item is a list with three properties: - major mode @@ -1088,7 +1327,7 @@ by specifying its :prefix property." :group 'smartparens) (defcustom sp-sexp-suffix nil - "Alist of major-mode specific suffix specification. + "Alist of `major-mode' specific suffix specification. Each item is a list with three properties: - major mode @@ -1134,7 +1373,7 @@ the sexp, but this is hardly ever what you want. You can add a post-handler on string pair and check for 'split-string action to add concatenation operators of the -language you work in (in each major-mode you can have a separate +language you work in (in each `major-mode' you can have a separate hook). For example, in PHP the string concatenation operator is a @@ -1161,10 +1400,18 @@ results in ;; hybrid lines (defcustom sp-hybrid-kill-excessive-whitespace nil - "If non-nil, `sp-kill-hybrid-sexp' will kill all whitespace up -until next hybrid sexp if the point is at the end of line or on a -blank line." - :type 'boolean + "Determine how `sp-kill-hybrid-sexp' kills excessive whitespace. + +If non-nil, `sp-kill-hybrid-sexp' will delete all whitespace +up until next hybrid sexp if the point is at the end of line or +on a blank line. + +When it is set to 'kill, whitespace will be appended to the sexp +in kill ring." + :type '(choice + (const :tag "Delete" t) + (const :tag "Kill" kill) + (const :tag "Off" nil)) :group 'smartparens) (defcustom sp-hybrid-kill-entire-symbol nil @@ -1174,7 +1421,7 @@ If t, always kill the symbol under point. If nil, never kill the entire symbol and only kill the part after point. -If a function, this should be a zero-arg predicate. When it +If a function, this should be a zero-arg predicate. When it returns non-nil value, we should kill from point." :type '(radio (const :tag "Always kill entire symbol" t) @@ -1210,20 +1457,31 @@ The value of `comment-start' is used if the major mode is not found." :type 'boolean :group 'smartparens) +(defcustom sp-echo-match-when-invisible t + "If non-nil, show-smartparens-mode prints the line of the +matching paren in the echo area if not visible on screen." + :type 'boolean + :group 'smartparens) + (defcustom sp-message-width 'frame - "Length of information and error messages to display. If set to -'frame (the default), messages are chosen based of the frame -width. `t' means chose the default (verbose) message, `nil' means -mute. Integers specify the maximum width." + "Length of information and error messages to display. + +If set to 'frame (the default), messages are chosen based of the +frame width. t means chose the default (verbose) message, nil +means mute. Integers specify the maximum width." :type '(choice (const :tag "Fit to frame" frame) (const :tag "Verbose" t) (const :tag "Mute" nil) (integer :tag "Max width")) :group 'smartparens) +;; TODO: this should be true by default > then the behaviour is +;; controlled by subword-mode... and this is a hard override (defcustom sp-use-subword nil - "If non-nill, `sp-kill-word' and `sp-backward-kill-word' only - kill \"subwords\" when `subword-mode' is active." + "Override of `subword-mode' killing behaviour. + +If non-nill, `sp-kill-word' and `sp-backward-kill-word' only +kill \"subwords\" when `subword-mode' is active." :type 'boolean :group 'smartparens) @@ -1236,21 +1494,234 @@ mute. Integers specify the maximum width." (or (and (boundp 'delete-selection-mode) delete-selection-mode) (and (boundp 'cua-delete-selection) cua-delete-selection cua-mode))) +(defun sp--delete-selection-supersede-p () + "Decide if the current command should delete the region or not. + +This check is used as value of 'delete-selection property on the +command symbol." + (if (or (equal current-prefix-arg '(4)) + (sp-region-ok-p (region-beginning) (region-end))) + 'supersede + (sp-message :unbalanced-region) + ;; Since this check runs in the pre-command-hook we can change the + ;; command to be executed... in this case we set it to ignore + ;; because we don't want to do anything. + (setq this-command 'ignore) + nil)) + +(defun sp--self-insert-uses-region-strict-p () + "Decide if the current `self-insert-command' should be able to +replace the region. + +This check is added to the special hook +`self-insert-uses-region-functions' which is checked by +`delete-selection-uses-region-p'." + (if (or (equal current-prefix-arg '(4)) + (sp-region-ok-p (region-beginning) (region-end))) + ;; region is OK or we are allowed to replace it, just say nil so + ;; that delsel handles this + nil + ;; in case region is bad we interrupt the insertion + (setq this-command 'ignore) + t)) + +;; TODO: this function was removed from Emacs, we should get rid of +;; the advice in time. (defadvice cua-replace-region (around fix-sp-wrap activate) + "Fix `sp-wrap' in `cua-selection-mode'." (if (sp-wrap--can-wrap-p) (cua--fallback) ad-do-it)) -(defadvice delete-selection-pre-hook (around fix-sp-wrap activate) - (unless (sp-wrap--can-wrap-p) +(defadvice cua-delete-region (around fix-sp-delete-region activate) + "If `smartparens-strict-mode' is enabled, perform a region +check before deleting." + (if smartparens-strict-mode + (progn + (unless (or current-prefix-arg + (sp-region-ok-p (region-beginning) (region-end))) + (user-error (sp-message :unbalanced-region :return))) + ad-do-it) ad-do-it)) + +(cl-eval-when (compile eval load) + (defun sp--get-substitute (struct list) + "Only ever call this from sp-get! This function does the +replacement of all the keywords with actual calls to sp-get." + (if (listp list) + (if (eq (car list) 'sp-get) + list + (mapcar (lambda (x) (sp--get-substitute struct x)) + (let ((command (car list))) + (cond + ((eq command 'sp-do-move-op) + (let ((argument (make-symbol "--sp-argument--"))) + `(let ((,argument ,(cadr list))) + (if (< ,argument :beg-prf) + (progn + (goto-char :beg-prf) + (delete-char (+ :op-l :prefix-l)) + (goto-char ,argument) + (insert :prefix :op)) + (goto-char ,argument) + (insert :prefix :op) + (goto-char :beg-prf) + (delete-char (+ :op-l :prefix-l)))))) + ((eq command 'sp-do-move-cl) + (let ((argument (make-symbol "--sp-argument--"))) + `(let ((,argument ,(cadr list))) + (if (> ,argument :end-in) + (progn + (goto-char ,argument) + (insert :cl :suffix) + (goto-char :end-in) + (delete-char (+ :cl-l :suffix-l))) + (goto-char :end-in) + (delete-char (+ :cl-l :suffix-l)) + (goto-char ,argument) + (insert :cl :suffix))))) + ((eq command 'sp-do-del-op) + `(progn + (goto-char :beg-prf) + (delete-char (+ :op-l :prefix-l)))) + ((eq command 'sp-do-del-cl) + `(progn + (goto-char :end-in) + (delete-char (+ :cl-l :suffix-l)))) + ((eq command 'sp-do-put-op) + `(progn + (goto-char ,(cadr list)) + (insert :prefix :op))) + ((eq command 'sp-do-put-cl) + `(progn + (goto-char ,(cadr list)) + (insert :cl :suffix))) + (t list))))) + (if (keywordp list) + (sp--get-replace-keyword struct list) + list))) + + (defun sp--get-replace-keyword (struct keyword) + (cl-case keyword + ;; point in buffer before the opening delimiter + (:beg `(plist-get ,struct :beg)) + ;; point in the buffer after the closing delimiter + (:end `(plist-get ,struct :end)) + ;; point in buffer after the opening delimiter + (:beg-in `(+ (plist-get ,struct :beg) (length (plist-get ,struct :op)))) + ;; point in buffer before the closing delimiter + (:end-in `(- (plist-get ,struct :end) (length (plist-get ,struct :cl)))) + ;; point in buffer before the prefix of this expression + (:beg-prf `(- (plist-get ,struct :beg) (length (plist-get ,struct :prefix)))) + ;; point in the buffer after the suffix of this expression + (:end-suf `(+ (plist-get ,struct :end) (length (plist-get ,struct :suffix)))) + ;; opening delimiter + (:op `(plist-get ,struct :op)) + ;; closing delimiter + (:cl `(plist-get ,struct :cl)) + ;; length of the opening pair + (:op-l `(length (plist-get ,struct :op))) + ;; length of the closing pair + (:cl-l `(length (plist-get ,struct :cl))) + ;; length of the entire expression, including enclosing + ;; delimiters and the prefix and suffix + (:len `(- (plist-get ,struct :end) + (plist-get ,struct :beg) + (- (length (plist-get ,struct :prefix))) + (- (length (plist-get ,struct :suffix))))) + ;; length of the the pair ignoring the prefix, including delimiters + (:len-out `(- (plist-get ,struct :end) (plist-get ,struct :beg))) + ;; length of the pair inside the delimiters + (:len-in `(- (plist-get ,struct :end) + (plist-get ,struct :beg) + (length (plist-get ,struct :op)) + (length (plist-get ,struct :cl)))) + ;; expression prefix + (:prefix `(plist-get ,struct :prefix)) + ;; expression prefix length + (:prefix-l `(length (plist-get ,struct :prefix))) + (:suffix `(plist-get ,struct :suffix)) + (:suffix-l `(length (plist-get ,struct :suffix))) + ;; combined op/cl and suffix/prefix + (:opp `(concat (plist-get ,struct :prefix) + (plist-get ,struct :op))) + (:opp-l `(+ (length (plist-get ,struct :prefix)) + (length (plist-get ,struct :op)))) + (:cls `(concat (plist-get ,struct :cl) + (plist-get ,struct :suffix))) + (:cls-l `(+ (length (plist-get ,struct :cl)) + (length (plist-get ,struct :suffix)))) + (t keyword)))) + + +;; TODO: rewrite this in terms of `symbol-macrolet' ?? +(defmacro sp-get (struct &rest forms) + "Get a property from a structure. + +STRUCT is a plist with the format as returned by `sp-get-sexp'. +Which means this macro also works with `sp-get-symbol', +`sp-get-string' and `sp-get-thing'. + +FORMS is an attribute we want to query. Currently supported +attributes are: + +:beg - point in buffer before the opening delimiter +:end - point in the buffer after the closing delimiter +:beg-in - point in buffer after the opening delimiter +:end-in - point in buffer before the closing delimiter +:beg-prf - point in buffer before the prefix of this expression +:end-suf - point in buffer after the suffix of this expression +:op - opening delimiter +:cl - closing delimiter +:op-l - length of the opening pair +:cl-l - length of the closing pair +:len - length of the entire expression, including enclosing + delimiters, the prefix and the suffix +:len-out - length of the the pair ignoring the prefix and suffix, + including delimiters +:len-in - length of the pair inside the delimiters +:prefix - expression prefix +:prefix-l - expression prefix length +:suffix - expression suffix +:suffix-l - expression suffix length + +These special \"functions\" are expanded to do the selected +action in the context of currently queried pair: + +Nullary: +\(sp-do-del-op) - remove prefix and opening delimiter +\(sp-do-del-cl) - remove closing delimiter and suffix + +Unary: +\(sp-do-move-op p) - move prefix and opening delimiter to point p +\(sp-do-move-cl p) - move closing delimiter and suffix to point p +\(sp-do-put-op p) - put prefix and opening delimiter at point p +\(sp-do-put-cl p) - put closing delimiter and suffix at point p + +In addition to these simple queries and commands, this macro +understands arbitrary forms where any of the aforementioned +attributes are used. Therefore, you can for example query for +\"(+ :op-l :cl-l)\". This query would return the sum of lengths +of opening and closing delimiter. A query +\"(concat :prefix :op)\" would return the string containing +expression prefix and the opening delimiter. + +Special care is taken to only evaluate the STRUCT argument once." + (declare (indent 1) + (debug (form body))) + (let ((st (make-symbol "struct"))) + (sp--get-substitute st `(let ((,st ,struct)) ,@forms)))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Misc/Utility functions (defun sp--indent-region (start end &optional column) - "Call `indent-region' unless `aggressive-indent-mode' is enabled." + "Call `indent-region' unless `aggressive-indent-mode' is enabled. + +START, END and COLUMN are the same as in `indent-region'." (unless (bound-and-true-p aggressive-indent-mode) ;; Don't issue "Indenting region..." message. (cl-letf (((symbol-function 'message) #'ignore)) @@ -1263,8 +1734,10 @@ This can be used with `sp-local-pair' calls to automatically insert the modes." (declare (indent 1) (debug (form body))) - `(progn - ,@(mapcar (lambda (form) (append (list (car form) arg) (cdr form))) forms))) + (let ((modes (make-symbol "modes"))) + `(let ((,modes ,arg)) + (progn + ,@(mapcar (lambda (form) (append (list (car form) modes) (cdr form))) forms))))) (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "(" (regexp-opt '("sp-with-modes" @@ -1273,22 +1746,32 @@ insert the modes." "\\_>") (1 font-lock-keyword-face)))) +(defmacro sp--with-case-sensitive (&rest body) + "Ensure that searching done within BODY is case-sensitive. + +Bind `case-fold-search' to nil if it is not already and avoid the +bind if it is already. Any function that needs to use any of the +sp--looking-* functions more than once should wrap them all in +`sp--with-case-sensitive'." + (declare (indent 0) + (debug (body))) + `(if case-fold-search + (let ((case-fold-search nil)) + ,@body) + ,@body)) + (defun sp--evil-normal-state-p () - "Checks to see if the current `evil-state' is in normal mode." + "Check to see if the current `evil-state' is in normal mode." (and (fboundp 'evil-normal-state-p) (evil-normal-state-p))) (defun sp--evil-motion-state-p () - "Checks to see if the current `evil-state' is in motion mode." + "Check to see if the current `evil-state' is in motion mode." (and (fboundp 'evil-motion-state-p) (evil-motion-state-p))) (defun sp--evil-visual-state-p () - "Checks to see if the current `evil-state' is in visual mode." + "Check to see if the current `evil-state' is in visual mode." (and (fboundp 'evil-visual-state-p) (evil-visual-state-p))) -(defun sp--reverse-string (str) - "Reverse the string STR." - (concat (reverse (append str nil)))) - (defun sp-point-in-blank-line (&optional p) "Return non-nil if line at point is blank (whitespace only). @@ -1312,13 +1795,46 @@ sexp, otherwise the call may be very slow." "\\`[ \t\n]*\\'" (buffer-substring-no-properties :beg-in :end-in)))))) +(defun sp-char-is-escaped-p (&optional point) + "Test if the char at POINT is escaped or not. + +POINT defaults to `point'." + (setq point (or point (point))) + (save-match-data + (when (save-excursion + (goto-char point) + (looking-back (concat sp-escape-char sp-escape-char "+") nil t)) + (eq (logand (length (match-string 0)) 1) 1)))) + +(defun sp--syntax-ppss (&optional p) + "Memoize the last result of `syntax-ppss'. + +P is the point at which we run `syntax-ppss'" + (let ((p (or p (point))) + (mem-p (sp-state-last-syntax-ppss-point sp-state))) + (if (and (eq p (nth 0 mem-p)) + (eq (point-min) (nth 1 mem-p)) + (eq (point-max) (nth 2 mem-p))) + (sp-state-last-syntax-ppss-result sp-state) + ;; Add hook to reset memoization if necessary + (unless (sp-state-last-syntax-ppss-point sp-state) + (add-hook 'before-change-functions 'sp--reset-memoization t t)) + (setf (sp-state-last-syntax-ppss-point sp-state) + (list p (point-min) (point-max)) + (sp-state-last-syntax-ppss-result sp-state) (syntax-ppss p))))) + (defun sp-point-in-string (&optional p) "Return non-nil if point is inside string or documentation string. +This function actually returns the 3rd element of `syntax-ppss' +which can be a number if the string is delimited by that +character or t if the string is delimited by general string +fences. + If optional argument P is present test this instead of point." (ignore-errors (save-excursion - (nth 3 (syntax-ppss p))))) + (nth 3 (sp--syntax-ppss p))))) (defun sp-point-in-comment (&optional p) "Return non-nil if point is inside comment. @@ -1327,27 +1843,29 @@ If optional argument P is present test this instead off point." (setq p (or p (point))) (ignore-errors (save-excursion - (or (nth 4 (syntax-ppss p)) - ;; this also test opening and closing comment delimiters... we - ;; need to chack that it is not newline, which is in "comment - ;; ender" class in elisp-mode, but we just want it to be - ;; treated as whitespace - (and (< p (point-max)) - (memq (char-syntax (char-after p)) '(?< ?>)) - (not (eq (char-after p) ?\n))) - ;; we also need to test the special syntax flag for comment - ;; starters and enders, because `syntax-ppss' does not yet - ;; know if we are inside a comment or not (e.g. / can be a - ;; division or comment starter...). - (-when-let (s (car (syntax-after p))) - (or (and (/= 0 (logand (lsh 1 16) s)) - (nth 4 (syntax-ppss (+ p 2)))) - (and (/= 0 (logand (lsh 1 17) s)) - (nth 4 (syntax-ppss (+ p 1)))) - (and (/= 0 (logand (lsh 1 18) s)) - (nth 4 (syntax-ppss (- p 1)))) - (and (/= 0 (logand (lsh 1 19) s)) - (nth 4 (syntax-ppss (- p 2)))))))))) + ;; We cannot be in a comment if we are inside a string + (unless (nth 3 (sp--syntax-ppss p)) + (or (nth 4 (sp--syntax-ppss p)) + ;; this also test opening and closing comment delimiters... we + ;; need to chack that it is not newline, which is in "comment + ;; ender" class in elisp-mode, but we just want it to be + ;; treated as whitespace + (and (< p (point-max)) + (memq (char-syntax (char-after p)) '(?< ?>)) + (not (eq (char-after p) ?\n))) + ;; we also need to test the special syntax flag for comment + ;; starters and enders, because `syntax-ppss' does not yet + ;; know if we are inside a comment or not (e.g. / can be a + ;; division or comment starter...). + (-when-let (s (car (syntax-after p))) + (or (and (/= 0 (logand (lsh 1 16) s)) + (nth 4 (syntax-ppss (+ p 2)))) + (and (/= 0 (logand (lsh 1 17) s)) + (nth 4 (syntax-ppss (+ p 1)))) + (and (/= 0 (logand (lsh 1 18) s)) + (nth 4 (syntax-ppss (- p 1)))) + (and (/= 0 (logand (lsh 1 19) s)) + (nth 4 (syntax-ppss (- p 2))))))))))) (defun sp-point-in-string-or-comment (&optional p) "Return non-nil if point is inside string, documentation string or a comment. @@ -1356,20 +1874,25 @@ If optional argument P is present, test this instead of point." (or (sp-point-in-string p) (sp-point-in-comment p))) +;; TODO: add -p suffix (defun sp-point-in-symbol (&optional p) - "Return non-nil if point is inside symbol. + "Return non-nil if `point' is inside symbol. + +If P is non-nil, interpret it as buffer position and test there. Point is inside symbol if characters on both sides of the point are in either word or symbol class." (setq p (or p (point))) (save-excursion (goto-char p) - (and (memq (char-syntax (following-char)) '(?w ?_)) + (and (/= 0 (following-char)) + (memq (char-syntax (following-char)) '(?w ?_)) (memq (char-syntax (preceding-char)) '(?w ?_))))) (defun sp--single-key-description (event) - "Return a description of the last event. Replace all the function -key symbols with garbage character (ň). + "Return a description of the last EVENT. + +Replace all the function key symbols with garbage character (ň). TODO: fix this!" (let ((original (single-key-description event))) @@ -1378,13 +1901,6 @@ TODO: fix this!" ((string-match-p "SPC" original) " ") (t original)))) -(defun sp--split-string (string by) - "Split STRING on BY. This simply calls `split-string' and if it -returns a list of length one, empty string is inserted to the -beginning." - (let ((sp (split-string string by))) - (if (not (cdr sp)) (cons "" sp) sp))) - ;; see https://github.com/Fuco1/smartparens/issues/125#issuecomment-20356176 (defun sp--current-indentation () "Get the indentation offset of the current line." @@ -1393,7 +1909,11 @@ beginning." (current-column))) (defun sp--calculate-indentation-offset (old-column old-indentation) - "Calculate correct indentation after re-indent." + "Calculate correct indentation after re-indent. + +OLD-COLUMN is the column before reindent. + +OLD-INDENTATION is the indentation depth before reindent." (let ((indentation (sp--current-indentation))) (cond ;; Point was in code, so move it along with the re-indented code @@ -1406,11 +1926,28 @@ beginning." (:else old-column)))) (defun sp--back-to-indentation (old-column old-indentation) + "Set the current column to proper value. + +See `sp--keep-indentation'. + +OLD-COLUMN is the column before reindent. + +OLD-INDENTATION is the indentation depth before reindent." (let ((offset (sp--calculate-indentation-offset old-column old-indentation))) (move-to-column offset))) +;; TODO: rename to preserve-current-column (defmacro sp--keep-indentation (&rest body) - "Execute BODY and restore the indentation." + "Execute BODY and restore the column. + +If point was in code move it along if the line is reinvented so +it is the same distance relative to first code column. + +If point was previously in the indentation region but would end +up in code, move it to the first code column. + +If point was in the indentation region and is still there after +BODY, do nothing." (declare (indent 0) (debug (body))) (let ((c (make-symbol "c")) @@ -1464,122 +2001,13 @@ The `sp--post-self-insert-hook-handler' is called in the (defun sp--special-self-insert-command-p () "Return non-nil if `this-command' is \"special\" self insert command. -A special self insert command is one that inserts a character but -does not trigger `post-self-insert-hook'." - (memq this-command sp--special-self-insert-commands)) - -(defun sp--signum (x) - "Return 1 if X is positive, -1 if negative, 0 if zero." - (cond ((> x 0) 1) ((< x 0) -1) (t 0))) - -(cl-eval-when (compile eval load) - (defun sp--get-substitute (struct list) - "Only ever call this from sp-get! This function do the -replacement of all the keywords with actual calls to sp-get." - (if (listp list) - (if (eq (car list) 'sp-get) - list - (mapcar (lambda (x) (sp--get-substitute struct x)) - (let ((command (car list))) - (cond - ((eq command 'sp-do-move-op) - (let ((argument (make-symbol "--sp-argument--"))) - `(let ((,argument ,(cadr list))) - (if (< ,argument :beg-prf) - (progn - (goto-char :beg-prf) - (delete-char (+ :op-l :prefix-l)) - (goto-char ,argument) - (insert :prefix :op)) - (goto-char ,argument) - (insert :prefix :op) - (goto-char :beg-prf) - (delete-char (+ :op-l :prefix-l)))))) - ((eq command 'sp-do-move-cl) - (let ((argument (make-symbol "--sp-argument--"))) - `(let ((,argument ,(cadr list))) - (if (> ,argument :end-in) - (progn - (goto-char ,argument) - (insert :cl :suffix) - (goto-char :end-in) - (delete-char (+ :cl-l :suffix-l))) - (goto-char :end-in) - (delete-char (+ :cl-l :suffix-l)) - (goto-char ,argument) - (insert :cl :suffix))))) - ((eq command 'sp-do-del-op) - `(progn - (goto-char :beg-prf) - (delete-char (+ :op-l :prefix-l)))) - ((eq command 'sp-do-del-cl) - `(progn - (goto-char :end-in) - (delete-char (+ :cl-l :suffix-l)))) - ((eq command 'sp-do-put-op) - `(progn - (goto-char ,(cadr list)) - (insert :prefix :op))) - ((eq command 'sp-do-put-cl) - `(progn - (goto-char ,(cadr list)) - (insert :cl :suffix))) - (t list))))) - (if (keywordp list) - (sp--get-replace-keyword struct list) - list))) +A special self insert command is one that inserts a character but +does not trigger `post-self-insert-hook'." + (memq this-command sp--special-self-insert-commands)) - (defun sp--get-replace-keyword (struct keyword) - (cl-case keyword - ;; point in buffer before the opening delimiter - (:beg `(plist-get ,struct :beg)) - ;; point in the buffer after the closing delimiter - (:end `(plist-get ,struct :end)) - ;; point in buffer after the opening delimiter - (:beg-in `(+ (plist-get ,struct :beg) (length (plist-get ,struct :op)))) - ;; point in buffer before the closing delimiter - (:end-in `(- (plist-get ,struct :end) (length (plist-get ,struct :cl)))) - ;; point in buffer before the prefix of this expression - (:beg-prf `(- (plist-get ,struct :beg) (length (plist-get ,struct :prefix)))) - ;; point in the buffer after the suffix of this expression - (:end-suf `(+ (plist-get ,struct :end) (length (plist-get ,struct :suffix)))) - ;; opening delimiter - (:op `(plist-get ,struct :op)) - ;; closing delimiter - (:cl `(plist-get ,struct :cl)) - ;; length of the opening pair - (:op-l `(length (plist-get ,struct :op))) - ;; length of the closing pair - (:cl-l `(length (plist-get ,struct :cl))) - ;; length of the entire expression, including enclosing - ;; delimiters and the prefix and suffix - (:len `(- (plist-get ,struct :end) - (plist-get ,struct :beg) - (- (length (plist-get ,struct :prefix))) - (- (length (plist-get ,struct :suffix))))) - ;; length of the the pair ignoring the prefix, including delimiters - (:len-out `(- (plist-get ,struct :end) (plist-get ,struct :beg))) - ;; length of the pair inside the delimiters - (:len-in `(- (plist-get ,struct :end) - (plist-get ,struct :beg) - (length (plist-get ,struct :op)) - (length (plist-get ,struct :cl)))) - ;; expression prefix - (:prefix `(plist-get ,struct :prefix)) - ;; expression prefix length - (:prefix-l `(length (plist-get ,struct :prefix))) - (:suffix `(plist-get ,struct :suffix)) - (:suffix-l `(length (plist-get ,struct :suffix))) - ;; combined op/cl and suffix/prefix - (:opp `(concat (plist-get ,struct :prefix) - (plist-get ,struct :op))) - (:opp-l `(+ (length (plist-get ,struct :prefix)) - (length (plist-get ,struct :op)))) - (:cls `(concat (plist-get ,struct :cl) - (plist-get ,struct :suffix))) - (:cls-l `(+ (length (plist-get ,struct :cl)) - (length (plist-get ,struct :suffix)))) - (t keyword)))) +(defun sp--signum (x) + "Return 1 if X is positive, -1 if negative, 0 if zero." + (cond ((> x 0) 1) ((< x 0) -1) (t 0))) ;; The structure returned by sp-get-sexp is a plist with following properties: ;; @@ -1593,88 +2021,40 @@ replacement of all the keywords with actual calls to sp-get." ;; exposed by the sp-get macro. This way, we can later change the ;; internal representation without much trouble. -;; TODO: rewrite this in terms of `symbol-macrolet' ?? -(defmacro sp-get (struct &rest forms) - "Get a property from a structure. - -STRUCT is a plist with the format as returned by `sp-get-sexp'. -Which means this macro also works with `sp-get-symbol', -`sp-get-string' and `sp-get-thing'. - -FORMS is an attribute we want to query. Currently supported -attributes are: - -:beg - point in buffer before the opening delimiter -:end - point in the buffer after the closing delimiter -:beg-in - point in buffer after the opening delimiter -:end-in - point in buffer before the closing delimiter -:beg-prf - point in buffer before the prefix of this expression -:end-suf - point in buffer after the suffix of this expression -:op - opening delimiter -:cl - closing delimiter -:op-l - length of the opening pair -:cl-l - length of the closing pair -:len - length of the entire expression, including enclosing - delimiters, the prefix and the suffix -:len-out - length of the the pair ignoring the prefix and suffix, - including delimiters -:len-in - length of the pair inside the delimiters -:prefix - expression prefix -:prefix-l - expression prefix length -:suffix - expression suffix -:suffix-l - expression suffix length - -These special \"functions\" are expanded to do the selected -action in the context of currently queried pair: - -Nullary: -\(sp-do-del-op) - remove prefix and opening delimiter -\(sp-do-del-cl) - remove closing delimiter and suffix - -Unary: -\(sp-do-move-op p) - move prefix and opening delimiter to point p -\(sp-do-move-cl p) - move closing delimiter and suffix to point p -\(sp-do-put-op p) - put prefix and opening delimiter at point p -\(sp-do-put-cl p) - put closing delimiter and suffix at point p - -In addition to these simple queries and commands, this macro -understands arbitrary forms where any of the aforementioned -attributes are used. Therefore, you can for example query for -\"(+ :op-l :cl-l)\". This query would return the sum of lengths -of opening and closing delimiter. A query -\"(concat :prefix :op)\" would return the string containing -expression prefix and the opening delimiter. - -Special care is taken to only evaluate the STRUCT argument once." - (declare (indent 1) - (debug (form body))) - (let ((st (make-symbol "struct"))) - (sp--get-substitute st `(let ((,st ,struct)) ,@forms)))) - (defmacro sp-compare-sexps (a b &optional fun what-a what-b) "Return non-nil if the expressions A and B are equal. Two expressions are equal if their :beg property is the same. -If optional argument WHAT is non-nil, use it as a keyword on -which to do the comparsion." +If optional argument FUN is non-nil, it is the comparison +function. + +If optional argument WHAT-A is non-nil, use it as a keyword on +which to do the comparsion (default to :beg). + +If optional argument WHAT-B is non-nil, use it as a keyword on +which to do the comparsion (default to WHAT-A)." (declare (debug (form form &optional functionp keywordp keywordp))) (setq fun (or fun 'equal)) (setq what-a (or what-a :beg)) (setq what-b (or what-b what-a)) `(,fun (sp-get ,a ,what-a) (sp-get ,b ,what-b))) -(defun sp-message (key) - "Display a message. The argument is either a string or list of -strings, or a keyword, in which case the string list is looked up -in `sp-message-alist'. The string to be displayed is chosen based -on the `sp-message-width' variable." +(defun sp-message (key &optional return) + "Display a message. + +KEY is either a string or list of strings, or a keyword, +in which case the string list is looked up in +`sp-message-alist'. The string to be displayed is chosen based on +the `sp-message-width' variable. + +If RETURN is non-nil return the string instead of printing it." (let ((msgs (cond ((listp key) key) ((stringp key) (list key)) (t (cdr (assq key sp-message-alist)))))) (when (and msgs sp-message-width) (if (eq sp-message-width t) - (message (car msgs)) + (if return (car msgs) (message "%s." (car msgs))) (let ((maxlen (if (eq sp-message-width 'frame) (frame-width) sp-message-width)) @@ -1684,14 +2064,15 @@ on the `sp-message-width' variable." (> (length msg) (length s))) (setf s msg))) (when s - (message s))))))) + (if return s (message "%s." s)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Adding/removing of pairs/bans/allows etc. -(defun sp--merge-prop (old-pair new-pair prop) +(defun sp--merge-prop (prop new-pair old-pair) "Merge a property PROP from NEW-PAIR into OLD-PAIR. + The list OLD-PAIR must not be nil." (let ((new-val (plist-get new-pair prop))) (cl-case prop @@ -1700,6 +2081,7 @@ The list OLD-PAIR must not be nil." (:suffix (plist-put old-pair :suffix new-val)) (:skip-match (plist-put old-pair :skip-match new-val)) (:trigger (plist-put old-pair :trigger new-val)) + (:trigger-wrap (plist-put old-pair :trigger-wrap new-val)) ((:actions :when :unless :pre-handlers :post-handlers) (cl-case (car new-val) (:add (plist-put old-pair prop (-union (plist-get old-pair prop) (cdr new-val)))) @@ -1723,12 +2105,13 @@ This modifies the OLD-PAIR by side effect." (let ((ind 0)) (--each new-pair (when (= 0 (% ind 2)) - (sp--merge-prop old-pair new-pair it)) + (sp--merge-prop it new-pair old-pair)) (setq ind (1+ ind)))) old-pair) -(defun sp--update-pair (old-pair new-pair) +(defun sp--update-pair (new-pair old-pair) "Copy properties from NEW-PAIR to OLD-PAIR. + The list OLD-PAIR must not be nil." (let ((ind 0)) (--each new-pair @@ -1743,10 +2126,11 @@ The list OLD-PAIR must not be nil." old-pair) (defun sp--update-pair-list (pair mode) - "Update the PAIR for major mode MODE. If this pair is not -defined yet for this major mode, add it. If this pair is already -defined, replace all the properties in the old definition with -values from PAIR." + "Update the PAIR for major mode MODE. + +If this pair is not defined yet for this major mode, add it. If +this pair is already defined, replace all the properties in the +old definition with values from PAIR." ;; get the structure relevant to mode. t means global setting (let ((struct (--first (eq mode (car it)) sp-pairs))) (if (not struct) @@ -1755,10 +2139,15 @@ values from PAIR." ;; properties. Open delimiter works as ID as usual. (let ((old-pair (--first (equal (plist-get pair :open) (plist-get it :open)) - (cdr struct)))) + (cdr struct))) + (open (plist-get pair :open))) (if (not old-pair) - (setcdr struct (cons pair (cdr struct))) - (sp--update-pair old-pair pair))))) + (progn + (unless (or (plist-get pair :close) + (sp--get-pair open (assq t sp-pairs))) + (error "Pair %s was never defined, please specify closing delimiter in instead of passing `nil'" open)) + (setcdr struct (cons pair (cdr struct)))) + (sp--update-pair pair old-pair))))) sp-pairs) (defun sp--get-pair (open list) @@ -1790,24 +2179,29 @@ If PROP is non-nil, return the value of that property instead." pair))) (defun sp-get-pair-definition (open mode &optional prop) - "Get the definition of pair identified by OPEN (opening -delimiter) for major mode MODE (or global definition if MODE is -t). + "Get the definition of pair identified by OPEN. + +OPEN is the opening delimiter, MODE is the major mode symbol or t +for global definition. If PROP is non-nil, return the value of that property instead." (sp--get-pair-definition open (cdr (assq mode sp-pairs)) prop)) (defun sp-get-pair (open &optional prop) - "Return the current value of pair defined by OPEN in the -current buffer, querying the variable `sp-local-pairs'. + "Return the definition of pair defined by OPEN in the current buffer. + +The value is fetched from `sp-local-pairs'. If PROP is non-nil, return the value of that property instead." (sp--get-pair-definition open sp-local-pairs prop)) -(defun sp--merge-with-local (mode) - "Merge the global pairs definitions with definitions for major mode MODE." - (let* ((global (cdr (assq t sp-pairs))) - (local (cdr (assq mode sp-pairs))) +(defun sp--merge-pair-configurations (specific &optional current) + "Merge SPECIFIC pair configuration to the CURRENT configuration. + +CURRENT defaults to `sp-local-pairs' if it is non-nil or the +global definition from `sp-pairs' if `sp-local-pairs' is nil." + (let* ((global (or current sp-local-pairs (cdr (assq t sp-pairs)))) + (local specific) (result nil)) ;; copy the pairs on global list first. This creates new plists ;; so we can modify them without changing the global "template" @@ -1838,7 +2232,7 @@ If PROP is non-nil, return the value of that property instead." (defun sp-wrap-with-pair (pair) "Wrap the following expression with PAIR. -This function is non-interactive helper. To use this function +This function is a non-interactive helper. To use this function interactively, bind the following lambda to a key: (lambda (&optional arg) (interactive \"P\") (sp-wrap-with-pair \"(\")) @@ -1878,6 +2272,7 @@ is wrapped instead. This is useful with selection functions in close &key trigger + trigger-wrap (actions '(wrap insert autoskip navigate)) when unless @@ -1900,6 +2295,8 @@ inserted if either OPEN or TRIGGER is typed. This is usually used as a shortcut for longer pairs or for pairs that can't be typed easily. +TRIGGER-WRAP is the same as TRIGGER but used for wrapping. + ACTIONS is a list of actions that smartparens will perform with this pair. Possible values are: @@ -1912,6 +2309,7 @@ this pair. Possible values are: characters in order. - navigate - enable this pair for navigation/highlight and strictness checks +- escape - allow autoescaping of this delimiter in string contexts If the ACTIONS argument has value :rem, the pair is removed. This can be used to remove default pairs you don't want to use. @@ -2019,27 +2417,27 @@ pair. The binding is added to the global map. You can also bind a similar lambda manually. To only bind this in specific major modes, use this property on `sp-local-pair' instead." (if (eq actions :rem) - (let ((remove (concat - (sp-get-pair-definition open t :open) - (sp-get-pair-definition open t :close))) - (global-list (assq t sp-pairs))) - (setcdr global-list (--remove (equal (plist-get it :open) open) (cdr global-list)))) + (let ((global-list (assq t sp-pairs))) + (setcdr global-list (--remove (equal (plist-get it :open) open) (cdr global-list))) + (--each (buffer-list) + (with-current-buffer it (sp--remove-local-pair open)))) (let ((pair nil)) (setq pair (plist-put pair :open open)) (when close (plist-put pair :close close)) (when trigger (plist-put pair :trigger trigger)) - (dolist (arg '((:actions . actions) - (:when . when) - (:unless . unless) - (:pre-handlers . pre-handlers) - (:post-handlers . post-handlers))) + (when trigger-wrap (plist-put pair :trigger-wrap trigger-wrap)) + (dolist (arg `((:actions . ,actions) + (:when . ,when) + (:unless . ,unless) + (:pre-handlers . ,pre-handlers) + (:post-handlers . ,post-handlers))) ;; We only consider "nil" as a proper value if the property ;; already exists in the pair. In that case, we will set it to ;; nil. This allows for removing properties in global ;; definitions. - (when (or (eval (cdr arg)) + (when (or (cdr arg) (sp-get-pair-definition open t (car arg))) - (plist-put pair (car arg) (eval (cdr arg))))) + (plist-put pair (car arg) (cdr arg)))) (sp--update-pair-list pair t)) (when (or wrap bind) (global-set-key (read-kbd-macro (or wrap bind)) `(lambda (&optional arg) @@ -2054,6 +2452,7 @@ modes, use this property on `sp-local-pair' instead." close &key trigger + trigger-wrap (actions '(:add)) (when '(:add)) (unless '(:add)) @@ -2093,7 +2492,7 @@ definition, this will override the global closing delimiter. The list arguments can optionally be of form starting with \":add\" or \":rem\" when these mean \"add to the global list\" -and \"remove from the global list\" respectivelly. Otherwise, +and \"remove from the global list\" respectively. Otherwise, the global list is replaced. If you wish to both add and remove things with single call, use \"((:add ...) (:rem ...))\" as an argument. Therefore, @@ -2132,20 +2531,19 @@ markdown when it signifies list item instead of emphasis. In addition, there is a global per major-mode option, see `sp-navigate-skip-match'." (if (eq actions :rem) - (let ((remove "")) - (dolist (m (-flatten (list modes))) - (setq remove (concat remove - (sp-get-pair-definition open m :open) - (sp-get-pair-definition open m :close))) - (let ((mode-pairs (assq m sp-pairs))) - (setcdr mode-pairs - (--remove (equal (plist-get it :open) open) - (cdr mode-pairs)))))) + (dolist (m (-flatten (list modes))) + (let ((mode-pairs (assq m sp-pairs))) + (setcdr mode-pairs + (--remove (equal (plist-get it :open) open) + (cdr mode-pairs)))) + (sp-with-buffers-using-mode m + (sp--remove-local-pair open))) (dolist (m (-flatten (list modes))) (let* ((pair nil)) (setq pair (plist-put pair :open open)) (when close (plist-put pair :close close)) (when trigger (plist-put pair :trigger trigger)) + (when trigger-wrap (plist-put pair :trigger-wrap trigger-wrap)) (when prefix (plist-put pair :prefix prefix)) (when suffix (plist-put pair :suffix suffix)) (when skip-match (plist-put pair :skip-match skip-match)) @@ -2231,7 +2629,7 @@ string and the action." (setcdr tag-list (--remove (equal trig (plist-get it :trigger)) (cdr tag-list))) (if (not tag) (setcdr tag-list (cons new-tag (cdr tag-list))) - (sp--update-pair tag new-tag))) + (sp--update-pair new-tag tag))) ;; mode doesn't exist (when actions (!cons (cons mode (list new-tag)) sp-tags)))))) @@ -2248,19 +2646,31 @@ string and the action." (defface sp-wrap-overlay-face '((t (:inherit sp-pair-overlay-face))) - "The face used to highlight wrap overlays." + "The face used to highlight wrap overlays. + +When the user wraps a region with multi-character pair a special +insertion mode is entered. This face is used for the overlays +where the possible wrappings are displayed. + +The opening and closing delimiters use +`sp-wrap-overlay-opening-pair' and `sp-wrap-overlay-closing-pair' +respectively." :group 'smartparens) (defface sp-wrap-overlay-opening-pair '((t (:inherit sp-wrap-overlay-face :foreground "green"))) - "The face used to highlight wrap overlays." + "The face used to highlight opening pairs for wrapping. + +See `sp-wrap-overlay-face'." :group 'smartparens) (defface sp-wrap-overlay-closing-pair '((t (:inherit sp-wrap-overlay-face :foreground "red"))) - "The face used to highlight wrap overlays." + "The face used to highlight closing pairs for wrapping. + +See `sp-wrap-overlay-face'." :group 'smartparens) (defface sp-wrap-tag-overlay-face @@ -2287,16 +2697,24 @@ works again as usual.") (defvar sp-pair-overlay-keymap (make-sparse-keymap) "Keymap for the pair overlays.") -(define-key sp-pair-overlay-keymap (kbd "C-g") 'sp-remove-active-pair-overlay) +(define-key sp-pair-overlay-keymap (kbd "C-g") + '(menu-item nil sp-remove-active-pair-overlay :filter + (lambda (cmd) + (unless (bound-and-true-p company-my-keymap) + cmd)))) (defvar sp-wrap-overlay-keymap (make-sparse-keymap) "Keymap for the wrap overlays.") (define-key sp-wrap-overlay-keymap (kbd "C-g") 'sp-wrap-cancel) (defun sp--overlays-at (&optional pos) - "Simple wrapper of `overlays-at' to get only overlays from -smartparens. Smartparens functions must use this function -instead of `overlays-at' directly." + "Wrapper around `overlays-at' to get smartparens overlays. + +POS is the same as for `overlays-at'. + +Smartparens functions must use this function instead of +`overlays-at' directly." + ;; TODO: we should probably also check the returned value (--filter (overlay-get it 'type) (overlays-at (or pos (point))))) (defun sp--point-in-overlay-p (overlay) @@ -2309,9 +2727,10 @@ instead of `overlays-at' directly." (- (overlay-end overlay) (overlay-start overlay))) (defun sp--get-active-overlay (&optional type) - "Get active overlay. Active overlay is the shortest overlay at -point. Optional argument TYPE restrict overlays to only those -with given type." + "Get active overlay. + +Active overlay is the shortest overlay at point. Optional +argument TYPE restrict overlays to only those with given type." (let ((overlays (sp--overlays-at))) (when type (setq overlays (--filter (eq (overlay-get it 'type) type) overlays))) @@ -2322,11 +2741,15 @@ with given type." (--reduce (if (< (sp--get-overlay-length it) (sp--get-overlay-length acc)) it acc) overlays))))) (defun sp--pair-overlay-create (start end id) - "Create an overlay over the currently inserted pair for -tracking the position of the point. START and END are the -boundaries of the overlay, ID is the id of the pair." + "Create an overlay over the currently inserted pair. + +This overlay is used for tracking the position of the point and +marks the active expression. START and END are the boundaries of +the overlay, ID is the id of the pair." (let ((overlay (make-overlay start end))) - (overlay-put overlay 'priority 100) + ;; set priority to 99 so that yasnippet with 100 overloads the + ;; keymap #625 + (overlay-put overlay 'priority 99) (overlay-put overlay 'keymap sp-pair-overlay-keymap) (overlay-put overlay 'pair-id id) (overlay-put overlay 'type 'pair) @@ -2339,7 +2762,7 @@ boundaries of the overlay, ID is the id of the pair." (interactive) (unwind-protect (-let (((obeg . oend) sp-wrap-overlays)) - (when (and (not (called-interactively-p)) + (when (and (not (called-interactively-p 'any)) (sp--delete-selection-p)) (kill-region (overlay-end obeg) (overlay-start oend))) (delete-region (overlay-start oend) (overlay-end oend)) @@ -2357,8 +2780,9 @@ boundaries of the overlay, ID is the id of the pair." (setq sp-wrap-overlays nil))) (defun sp--pair-overlay-fix-highlight () - "Fix highlighting of the pair overlays. Only the active overlay -should be highlighted." + "Fix highlighting of the pair overlays. + +Only the active overlay should be highlighted." (--each (sp--overlays-at) (overlay-put it 'face nil)) (let* ((active (sp--get-active-overlay)) (type (and active (overlay-get active 'type)))) @@ -2376,8 +2800,13 @@ should be highlighted." (overlay-put (car sp-wrap-tag-overlays) 'face 'sp-wrap-tag-overlay-face))))) (defun sp--pair-overlay-post-command-handler () - "Remove all pair overlays that doesn't have point inside them, -are of zero length, or if point moved backwards." + "Remove all invalid pair overlays. + +An invalid overlay is one that doesn't have point inside it or +is of zero length. + +Also remove all pair overlays if point moved backwards and +`sp-cancel-autoskip-on-backward-movement' is non-nil." ;; if the point moved backwards, remove all overlays (if (and sp-cancel-autoskip-on-backward-movement (< (point) sp-previous-point)) @@ -2391,6 +2820,14 @@ are of zero length, or if point moved backwards." (when sp-pair-overlay-list (setq sp-previous-point (point)))) +(defun sp--reset-memoization (&rest ignored) + "Reset memoization as a safety precaution. + +IGNORED is a dummy argument used to eat up arguments passed from +the hook where this is executed." + (setf (sp-state-last-syntax-ppss-point sp-state) nil + (sp-state-last-syntax-ppss-result sp-state) nil)) + (defun sp-remove-active-pair-overlay () "Deactivate the active overlay. See `sp--get-active-overlay'." (interactive) @@ -2427,87 +2864,110 @@ are of zero length, or if point moved backwards." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Action predicates -(defun sp-in-string-p (id action context) +(defun sp-in-string-p (_id _action context) "Return t if point is inside string or comment, nil otherwise." (eq context 'string)) -(defun sp-in-docstring-p (id action context) +(defun sp-in-string-quotes-p (_id action context) + "Special string test for quotes. + +On insert action, test the string context one character back from +point. Return nil at `bobp'. + +On escape action use the value of CONTEXT." + (cond + ((eq action 'insert) + (if (bobp) nil + (save-excursion (backward-char 1) (sp-point-in-string)))) + ((eq action 'escape) + (eq context 'string)))) + +(defun sp-in-docstring-p (_id _action context) "Return t if point is inside elisp docstring, nil otherwise." (and (eq context 'string) (save-excursion - (goto-char (car (sp-get-quoted-string-bounds))) - (ignore-errors (backward-sexp 3)) - (looking-at-p (regexp-opt '("defun" "defmacro" - "cl-defun" "cl-defmacro" - "defun*" "defmacro*" - "lambda" "-lambda")))))) - -(defun sp-in-code-p (id action context) + (--when-let (car (sp-get-quoted-string-bounds)) + (goto-char it) + (ignore-errors (backward-sexp 3)) + (looking-at-p (regexp-opt '("defun" "defmacro" + "cl-defun" "cl-defmacro" + "defun*" "defmacro*" + "lambda" "-lambda"))))))) + +(defun sp-in-code-p (_id _action context) "Return t if point is inside code, nil otherwise." (eq context 'code)) -(defun sp-in-comment-p (id action context) +(defun sp-in-comment-p (_id _action context) "Return t if point is inside comment, nil otherwise." (eq context 'comment)) -(defun sp-in-math-p (id action context) +(defun sp-in-math-p (_id _action _context) "Return t if point is inside code, nil otherwise." (when (functionp 'texmathp) (texmathp))) -(defun sp-point-before-eol-p (id action context) +(defun sp-point-before-eol-p (_id action _context) "Return t if point is followed by optional white spaces and end of line, nil otherwise. This predicate is only tested on \"insert\" action." (when (eq action 'insert) (sp--looking-at-p "\\s-*$"))) -(defun sp-point-after-bol-p (id action context) +(defun sp-point-after-bol-p (id action _context) "Return t if point follows beginning of line and possibly white spaces, nil otherwise. This predicate is only tested on \"insert\" action." (when (eq action 'insert) (sp--looking-back-p (concat "^\\s-*" (regexp-quote id))))) -(defun sp-point-at-bol-p (id action context) +(defun sp-point-at-bol-p (id action _context) "Return t if point is at the beginning of line, nil otherwise. This predicate is only tested on \"insert\" action." (when (eq action 'insert) (sp--looking-back-p (concat "^" (regexp-quote id))))) -(defun sp-point-before-symbol-p (id action context) +(defun sp-point-before-symbol-p (_id action _context) "Return t if point is followed by a symbol, nil otherwise. This predicate is only tested on \"insert\" action." (when (eq action 'insert) (sp--looking-at-p "\\s_"))) -(defun sp-point-before-word-p (id action context) +(defun sp-point-before-word-p (_id action _context) "Return t if point is followed by a word, nil otherwise. This predicate is only tested on \"insert\" action." (when (eq action 'insert) (sp--looking-at-p "\\sw\\|\\s_"))) -(defun sp-point-after-word-p (id action context) +(defun sp-point-after-word-p (id action _context) "Return t if point is after a word, nil otherwise. This predicate is only tested on \"insert\" action." - (when (eq action 'insert) + ;; TODO: remove condition with sp-defpair + (when (memq action '(insert escape)) (sp--looking-back-p (concat "\\(\\sw\\|\\s_\\)" (regexp-quote id))))) -(defun sp-point-before-same-p (id action context) +(defun sp-point-before-same-p (id action _context) "Return t if point is followed by ID, nil otherwise. This predicate is only tested on \"insert\" action." (when (eq action 'insert) (sp--looking-at-p (regexp-quote id)))) -(defun sp-point-in-empty-line-p (id action context) - "Return t if point is on an empty line, nil otherwise" +(defun sp-point-in-empty-line-p (id _action _context) + "Return t if point is on an empty line, nil otherwise." (and (sp--looking-at-p "\\s-*$") (sp--looking-back-p (concat "^\\s-*" (regexp-quote id))))) +(defun sp-char-escaped-p (_id action _context) + "Return non-nil if character before point is escaped with \\." + (when (eq action 'insert) + (save-excursion + (backward-char 1) + (looking-back "\\\\" 1)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Pair insertion/deletion/skipping (defun sp--do-action-p (id action &optional use-inside-string) - "Return t if action ACTION can be performed with pair ID. + "Return t if pair ID can perform ACTION. If ACTION is a list, return t if at least one action from the list can be performed. @@ -2532,9 +2992,9 @@ If USE-INSIDE-STRING is non-nil, use value of (setq r (when (memq a actions) ;;(and (when-clause) (not (unless-clause))) (and (or (not when-l) - (run-hook-with-args-until-success 'when-l id a context)) + (--some (funcall it id a context) when-l)) (or (not unless-l) - (not (run-hook-with-args-until-success 'unless-l id a context)))))) + (not (--some (funcall it id a context) unless-l)))))) (!cdr action)) r)) @@ -2569,7 +3029,6 @@ value is used instead of a test." (cl-labels ((push-non-empty (what) (unless (equal (cadr what) "") - ;; relies on dynamic binding (push what spec)))) (with-temp-buffer (insert fun) @@ -2622,143 +3081,172 @@ see `sp-pair' for description." ((stringp fun) (eval (sp--parse-insertion-spec fun))))) -(defun sp--run-hook-with-args (id type action) - "Run all the hooks for pair ID of type TYPE on action ACTION." + +(defvar sp-handler-context nil + "Special variable holding context during handler execution.") + +;; TODO: get rid of `sp-handler-context' and make all the handlers (we +;; should call them hooks) take better arguments, what we pass now is +;; useless almost always +(defun sp--run-hook-with-args (id type action &optional context-values) + "Run all the hooks for pair ID of type TYPE on action ACTION. + +CONTEXT-VALUES is a plist with arbitrary values (depending on the +action). A dynamic varable `sp-handler-context' will be bound to +this value during execution of the handler." (ignore-errors (let ((hook (sp-get-pair id type)) (context (sp--get-handler-context type))) (if hook - (--each hook (sp--run-function-or-insertion it id action context)) - ;; TODO: WHAT THE FUCK IS THIS ???11? - (let ((tag-hook (plist-get - (--first (string-match-p - (replace-regexp-in-string "_" ".*?" (plist-get it :open)) - id) - (cdr (assq 'html-mode sp-tags))) ;; REALLY? - type))) - (run-hook-with-args 'tag-hook id action context)))))) + (let ((sp-handler-context context-values)) + (--each hook (sp--run-function-or-insertion it id action context))) + (run-hook-with-args 'tag-hook id action context))))) ;; TODO: add a test for a symbol property that would tell this handler ;; not to re=set `sp-last-operation'. Useful for example in "macro -;; funcions" like `my-wrap-with-paren'. +;; functions" like `my-wrap-with-paren'. (defun sp--post-command-hook-handler () "Handle the situation after some command has executed." - (when (sp--special-self-insert-command-p) - (sp--post-self-insert-hook-handler)) - (ignore-errors - (when smartparens-mode - ;; handle the wrap overlays - (when sp-wrap-overlays - (let* ((overlay (car sp-wrap-overlays)) - (start (overlay-start overlay)) - (end (overlay-end overlay)) - (p (point))) - (when (or (< p sp-previous-point) - (> p end) - (< p start)) - (sp-wrap-cancel)))) - (when sp-wrap-overlays - (setq sp-previous-point (point))) - - ;; Here we run the delayed hooks. See issue #80 - (when (and (not (eq sp-last-operation 'sp-insert-pair)) - sp-last-inserted-pair) - (let ((hooks (sp-get-pair sp-last-inserted-pair :post-handlers-cond))) - (--each hooks - (let ((fun (car it)) - (conds (cdr it))) - (when (or (--any? (eq this-command it) conds) - (--any? (equal (single-key-description last-command-event) it) conds)) - (sp--run-function-or-insertion - fun sp-last-inserted-pair 'insert - (sp--get-handler-context :post-handlers)))))) - (setq sp-last-inserted-pair nil)) - - ;; Here we run the delayed insertion. Some details in issue #113 - (when (and (not (eq sp-last-operation 'sp-insert-pair-delayed)) - sp-delayed-pair) - (let* ((pair (car sp-delayed-pair)) - (beg (cdr sp-delayed-pair)) - (conds (sp-get-pair pair :when-cond)) - (open-pair pair) - (close-pair (sp-get-pair pair :close))) - (when (and conds - (--any? (cond - ((and (commandp it) - (not (stringp it))) - (eq this-command it)) - ((stringp it) - (equal (single-key-description last-command-event) it)) - ((ignore-errors (funcall it pair 'insert (sp--get-handler-context :post-handlers))))) conds)) - ;; TODO: refactor this and the same code in - ;; `sp-insert-pair' to a separate function - (sp--run-hook-with-args open-pair :pre-handlers 'insert) - (insert close-pair) - (backward-char (length close-pair)) - (sp--pair-overlay-create beg - (+ (point) (length close-pair)) - open-pair) - ;; no auto-escape here? Should be fairly safe - (sp--run-hook-with-args open-pair :post-handlers 'insert) - (setq sp-last-inserted-pair open-pair) - (setq sp-last-operation 'sp-insert-pair))) - (setq sp-delayed-pair nil)) - - (when (eq sp-last-operation 'sp-insert-pair-delayed) - (setq sp-last-operation nil)) - - (unless (or (sp--self-insert-command-p) - (sp--special-self-insert-command-p)) - ;; unless the last command was a self-insert, remove the - ;; information about the last wrapped region. It is only used - ;; for: 1. deleting the wrapping immediately after the wrap, - ;; 2. re-wrapping region immediatelly after a sucessful wrap. - ;; Therefore, the deletion should have no ill-effect. If the - ;; necessity will arise, we can add a different flag. - (setq sp-last-wrapped-region nil) - (setq sp-last-operation nil)) - - (when show-smartparens-mode - (if (member this-command sp-show-enclosing-pair-commands) - (sp-show--pair-enc-function) - (when (not (eq this-command 'sp-highlight-current-sexp)) - (sp-show--pair-delete-enc-overlays))))))) + (sp--with-case-sensitive + (when (sp--special-self-insert-command-p) + (sp--post-self-insert-hook-handler)) + (ignore-errors + (when smartparens-mode + ;; handle the wrap overlays + (when sp-wrap-overlays + (let* ((overlay (car sp-wrap-overlays)) + (start (overlay-start overlay)) + (end (overlay-end overlay)) + (p (point))) + (when (or (< p sp-previous-point) + (> p end) + (< p start)) + (sp-wrap-cancel)))) + (when sp-wrap-overlays + (setq sp-previous-point (point))) + + ;; Here we run the delayed hooks. See issue #80 + (cond + ((eq (car-safe (sp-state-delayed-hook sp-state)) :next) + (setf (car (sp-state-delayed-hook sp-state)) :this)) + ((eq (car-safe (sp-state-delayed-hook sp-state)) :this) + (let* ((pair (cdr (sp-state-delayed-hook sp-state))) + (hooks (sp-get-pair pair :post-handlers-cond))) + (--each hooks + (let ((fun (car it)) + (conds (cdr it))) + (when (or (--any? (eq this-command it) conds) + (--any? (equal (single-key-description last-command-event) it) conds)) + (sp--run-function-or-insertion + fun pair 'insert + (sp--get-handler-context :post-handlers))))) + (setf (sp-state-delayed-hook sp-state) nil) + (setq sp-last-inserted-pair nil)))) + + ;; Here we run the delayed insertion. Some details in issue #113 + (when (and (not (eq sp-last-operation 'sp-insert-pair-delayed)) + sp-delayed-pair) + (let* ((pair (car sp-delayed-pair)) + (beg (cdr sp-delayed-pair)) + (conds (sp-get-pair pair :when-cond)) + (open-pair pair) + (close-pair (sp-get-pair pair :close))) + (when (and conds + (--any? (cond + ((and (commandp it) + (not (stringp it))) + (eq this-command it)) + ((stringp it) + (equal (single-key-description last-command-event) it)) + ((ignore-errors (funcall it pair 'insert (sp--get-handler-context :post-handlers))))) conds)) + ;; TODO: refactor this and the same code in + ;; `sp-insert-pair' to a separate function + (sp--run-hook-with-args open-pair :pre-handlers 'insert) + (insert close-pair) + (backward-char (length close-pair)) + (sp--pair-overlay-create beg + (+ (point) (length close-pair)) + open-pair) + ;; no auto-escape here? Should be fairly safe + (sp--run-hook-with-args open-pair :post-handlers 'insert) + (setq sp-last-inserted-pair open-pair) + ;; TODO: this is probably useless + (setq sp-last-operation 'sp-insert-pair))) + (setq sp-delayed-pair nil)) + + (when (eq sp-last-operation 'sp-insert-pair-delayed) + (setq sp-last-operation nil)) + + (unless (or (sp--self-insert-command-p) + (sp--special-self-insert-command-p)) + ;; unless the last command was a self-insert, remove the + ;; information about the last wrapped region. It is only used + ;; for: 1. deleting the wrapping immediately after the wrap, + ;; 2. re-wrapping region immediatelly after a sucessful wrap. + ;; Therefore, the deletion should have no ill-effect. If the + ;; necessity will arise, we can add a different flag. + (setq sp-last-wrapped-region nil) + (setq sp-last-operation nil)) + + (when show-smartparens-mode + (if (member this-command sp-show-enclosing-pair-commands) + (sp-show--pair-enc-function) + (when (not (eq this-command 'sp-highlight-current-sexp)) + (sp-show--pair-delete-enc-overlays)))))))) (defmacro sp--setaction (action &rest forms) + "Use ACTION as a flag to evaluating FORMS. + +If ACTION is nil, evaluate FORMS and set it to the value of the +last form; otherwise do nothing." (declare (debug (form body))) - `(unless action - (setq action (progn ,@forms)))) + `(unless ,action + (setq ,action (progn ,@forms)))) ;; TODO: this introduces a regression, where doing C-4 [ inserts [[[[] ;; figure out how to detect the argument to self-insert-command that ;; resulted to this insertion (defun sp--post-self-insert-hook-handler () + "Handler for `post-self-insert-hook'." (with-demoted-errors "sp--post-self-insert-hook-handler: %S" (when smartparens-mode - (let (op action) - (setq op sp-last-operation) - (when (region-active-p) - (sp-wrap--initialize)) - (cond - (sp-wrap-overlays - (sp-wrap)) - (t - ;; TODO: this does not pick correct pair!! it uses insert and not wrapping code - (sp--setaction action (-when-let ((_ . open-pairs) (sp--all-pairs-to-insert)) - (catch 'done - (-each open-pairs - (-lambda ((&keys :open open :close close)) - (--when-let (sp--wrap-repeat-last (cons open close)) - (throw 'done it))))))) - (sp--setaction action (sp-insert-pair)) - (sp--setaction action (sp-skip-closing-pair)) - ;; if nothing happened, we just inserted a character, so - ;; set the apropriate operation. We also need to check - ;; for `sp--self-insert-no-escape' not to overwrite - ;; it. See `sp-autoinsert-quote-if-followed-by-closing-pair'. - (when (and (not action) - (not (eq sp-last-operation 'sp-self-insert-no-escape))) - (setq sp-last-operation 'sp-self-insert)))))))) + (sp--with-case-sensitive + (catch 'done + (let (action) + (when (region-active-p) + (condition-case err + (sp-wrap--initialize) + (user-error + (message (error-message-string err)) + ;; we need to remove the undo record of the insertion + (unless (eq buffer-undo-list t) + ;; pop all undo info until we hit an insertion node + (sp--undo-pop-to-last-insertion-node) + ;; get rid of it and insert an undo boundary marker + (pop buffer-undo-list) + (undo-boundary)) + (restore-buffer-modified-p sp-buffer-modified-p) + (throw 'done nil)))) + (cond + (sp-wrap-overlays + (sp-wrap)) + (t + ;; TODO: this does not pick correct pair!! it uses insert and not wrapping code + (sp--setaction + action + (-when-let ((_ . open-pairs) (sp--all-pairs-to-insert nil 'wrap)) + (catch 'done + (-each open-pairs + (-lambda ((&keys :open open :close close)) + (--when-let (sp--wrap-repeat-last (cons open close)) + (throw 'done it))))))) + (unless overwrite-mode (sp--setaction action (sp-insert-pair))) + (sp--setaction action (sp-skip-closing-pair)) + (unless action (sp-escape-open-delimiter)) + ;; if nothing happened, we just inserted a character, so + ;; set the apropriate operation. + (unless action + (setq sp-last-operation 'sp-self-insert)))))))))) ;; Unfortunately, some modes rebind "inserting" keys to their own ;; handlers but do not hand over the insertion back to @@ -2771,39 +3259,41 @@ see `sp-pair' for description." ;; TODO: make a proper data structure for state tracking and describe ;; why we need each of these. (defun sp--save-pre-command-state () + "Save some of the buffer state before `pre-command-hook'." (setq sp-point-inside-string (sp-point-in-string)) (setq sp-pre-command-point (point)) (setq sp-buffer-modified-p (buffer-modified-p))) (add-hook 'pre-command-hook 'sp--save-pre-command-state) -(defun sp--pre-command-hook-handler () - "Main handler of pre-command-hook. - -Handle the `delete-selection-mode' or `cua-delete-selection' -stuff here.") - (defun sp--get-pair-list () - "Return all pairs that are recognized in this -`major-mode' and do not have same opening and closing delimiter. -This is used for navigation functions." + "Get all non-stringlike pairs. + +Return all pairs that are recognized in this `major-mode' and do +not have same opening and closing delimiter. This is used for +navigation functions." (--filter (not (string= (car it) (cdr it))) sp-pair-list)) (defun sp--get-stringlike-list () - "Return all pairs that are recognized in this `major-mode' that + "Get all string-like pairs. + +Return all pairs that are recognized in this `major-mode' that have same opening and closing delimiter." (--filter (string= (car it) (cdr it)) sp-pair-list)) (defun sp--get-allowed-pair-list () - "Return all pairs that are recognized in this -`major-mode', do not have same opening and closing delimiter and -are allowed in the current context. See also -`sp--get-pair-list'." + "Get all allowed non string-like pairs. + +Return all pairs that are recognized in this `major-mode', do not +have same opening and closing delimiter and are allowed in the +current context. See also `sp--get-pair-list'." (--filter (and (sp--do-action-p (car it) 'navigate) (not (equal (car it) (cdr it)))) sp-pair-list)) (defun sp--get-allowed-stringlike-list () - "Return all pairs that are recognized in this `major-mode', + "Get all allowed string-like pairs. + +Return all pairs that are recognized in this `major-mode', have the same opening and closing delimiter and are allowed in the current context." (--filter (and (sp--do-action-p (car it) 'navigate) @@ -2834,20 +3324,22 @@ extra boundary conditions depending on parens." (defun sp--strict-regexp-opt (strings &optional ignored) "Like regexp-opt, but with extra boundary conditions to ensure that the strings are not matched in-symbol." - (with-syntax-table - ;; HACK: this is a terrible hack to make ' be treated as a - ;; punctuation. Many text modes set it as word character which - ;; messes up the regexps - (let ((table (make-syntax-table (syntax-table)))) - (modify-syntax-entry ?' "." table) - table) - (--> strings - (-group-by (lambda (string) - (list (and (string-match-p "\\`\\<" string) t) - (and (string-match-p "\\>\\'" string) t))) - it) - (mapconcat (lambda (g) (apply 'sp--regexp-for-group g)) it "\\|") - (concat "\\(?:" it "\\)")))) + (if strings + (with-syntax-table + ;; HACK: this is a terrible hack to make ' be treated as a + ;; punctuation. Many text modes set it as word character which + ;; messes up the regexps + (let ((table (make-syntax-table (syntax-table)))) + (modify-syntax-entry ?' "." table) + table) + (--> strings + (-group-by (lambda (string) + (list (and (string-match-p "\\`\\<" string) t) + (and (string-match-p "\\>\\'" string) t))) + it) + (mapconcat (lambda (g) (apply 'sp--regexp-for-group g)) it "\\|") + (concat "\\(?:" it "\\)"))) + "^\\<$")) (defun sp--strict-regexp-quote (string) "Like regexp-quote, but make sure that the string is not @@ -2870,10 +3362,13 @@ delimiter for any pair allowed in current context." (sp--strict-regexp-opt (--mapcat (list (car it) (cdr it)) pair-list))) (cl-defun sp--get-stringlike-regexp (&optional (pair-list (sp--get-allowed-stringlike-list))) - (regexp-opt (--map (car it) pair-list))) + "Return a regexp matching any string-like delimiter. -(defun sp-pair-is-stringlike-p (delim) - (--first (equal delim (car it)) (sp--get-allowed-stringlike-list))) +In case PAIR-LIST is empty return a regexp that never matches +anything." + (if (consp pair-list) + (regexp-opt (--map (car it) pair-list)) + "^\\<$")) (defun sp--get-last-wraped-region (beg end open close) "Return `sp-get-sexp' style plist about the last wrapped region. @@ -2904,19 +3399,21 @@ provided values." "Return non-nil if we can wrap a region. This is used in advices on various pre-command-hooks from -\"selection deleting\" modes to intercept their actions." - (--any? (or (string-prefix-p (sp--single-key-description last-command-event) (car it)) - (string-prefix-p (sp--single-key-description last-command-event) (cdr it))) - (sp--get-pair-list-wrap))) +\"selection deleting\" modes to intercept their actions. + +Also added to `self-insert-uses-region-functions' to prevent +`delete-selection-mode' from replacing the region." + (let* ((list (sp--get-pair-list-wrap)) + (desc (sp--single-key-description last-command-event))) + (--any? (or (string-prefix-p desc (car it)) + (string-prefix-p desc (cdr it))) + list))) (defun sp--pair-to-wrap-comparator (prop a b) "Comparator for wrapping pair selection. PROP specifies wrapping-end. A and B are pairs to be compared." - (let ((rprop (if (eq prop :open) :close :open))) - (if (< (length (plist-get it prop)) (length (plist-get other prop))) - (string-prefix-p (plist-get it rprop) (plist-get other rprop)) - (not (string-prefix-p (plist-get other rprop) (plist-get it rprop)))))) + (< (length (plist-get a prop)) (length (plist-get b prop)))) (defun sp--pair-to-wrap (&optional prefix) "Return information about possible wrapping pairs. @@ -2930,6 +3427,19 @@ overlay." (obeg (car sp-wrap-overlays)) (prefix (or prefix (sp--get-overlay-text obeg))) (opening-pairs (--filter (string-prefix-p prefix (plist-get it :open)) working-pairs)) + ;; HACK: Here, we will add special "trigger pairs" to the + ;; opening list. We set the opening delimiter to the + ;; trigger, leave the rest alone and put the real open into + ;; :open-real property. When we get the pair back, we will + ;; check this property, and if present, fix the pair back to + ;; the regular form + (wrapper-pairs (->> (--filter (string-prefix-p prefix (or (plist-get it :trigger-wrap) "")) working-pairs) + (-map (-lambda ((pair &as &plist :open open :trigger-wrap trigger-wrap)) + (setq pair (copy-sequence pair)) + (setq pair (plist-put pair :open trigger-wrap)) + (setq pair (plist-put pair :open-real open)) + pair)))) + (opening-pairs (-concat wrapper-pairs opening-pairs)) (closing-pairs (--filter (string-prefix-p prefix (plist-get it :close)) working-pairs)) (open (car (--sort (sp--pair-to-wrap-comparator :open it other) opening-pairs))) ;; TODO: do we need the special sorting here? @@ -2951,6 +3461,44 @@ overlay." ;; TODO: get rid of the following variables (setq sp-wrap-point (- (point) inserted-string-length)) (setq sp-wrap-mark (mark)) + ;; balance check + (with-silent-modifications + (let ((inserted-string + (prog1 (delete-and-extract-region sp-wrap-point (point)) + ;; HACK: in modes with string fences, the insertion + ;; of the delimiter causes `syntax-propertize' to + ;; fire, but the above deletion doesn't re-run it + ;; because the cache tells it the state is OK. We + ;; need to destroy the cache and re-run the + ;; `syntax-propertize' on the buffer. This might be + ;; expensive, but we only done this on wrap-init so + ;; it's fine, I guess. + (setq syntax-propertize--done -1) + (syntax-propertize (point-max)))) + (point-string-context (sp-get-quoted-string-bounds sp-wrap-point)) + (mark-string-context (sp-get-quoted-string-bounds (mark)))) + ;; If point and mark are inside the same string, we don't + ;; need to check if the region is OK. If both are outisde + ;; strings, we have to. If one is inside and the other is + ;; not, no matter what we would break, so we exit. + (cond + ;; inside the same string + ((and point-string-context mark-string-context + (eq (car point-string-context) + (car mark-string-context)))) + ;; neither is inside string + ((and (not point-string-context) + (not mark-string-context)) + (unless (sp-region-ok-p sp-wrap-point (mark)) + (user-error "Mismatched sexp state: wrapping would break structure"))) + ;; one is in and the other isn't + ((if point-string-context (not mark-string-context) mark-string-context) + (user-error "Mismatched string state: point %sin string, mark %sin string" + (if (car-safe point-string-context) "" "not ") + (if (car-safe mark-string-context) "" "not "))) + ;; both are in but in different strings + (t (user-error "Mismatched string state: point and mark are inside different strings"))) + (insert inserted-string))) ;; if point > mark, we need to move point to mark and reinsert the ;; just inserted character. (when (> (point) (mark)) @@ -2968,6 +3516,7 @@ overlay." (overlay-put oright 'priority 100) (overlay-put oleft 'keymap sp-wrap-overlay-keymap) (overlay-put oleft 'type 'wrap) + (setq sp-previous-point (point)) (goto-char (1+ (overlay-start oleft))))))) (defun sp-wrap--finalize (wrapping-end open close) @@ -2991,10 +3540,14 @@ OPEN and CLOSE are the delimiters." (cond ((eq wrapping-end :open) (if sp-wrap-respect-direction - (goto-char (overlay-start obeg)) + (progn + (set-mark (overlay-end oend)) + (goto-char (overlay-start obeg))) (when (> sp-wrap-point sp-wrap-mark) + (set-mark (overlay-start obeg)) (goto-char (overlay-end oend))))) ((eq wrapping-end :close) + (set-mark (overlay-start obeg)) (goto-char (overlay-end oend)))) (sp-wrap--clean-overlays) (sp--run-hook-with-args open :post-handlers 'wrap))) @@ -3010,19 +3563,23 @@ programatically. Use `sp-wrap-with-pair' instead." ((obeg . oend) sp-wrap-overlays)) (cond (open - (-let (((&plist :open open :close close) open)) + (-let (((&plist :open open :close close :open-real open-real) open)) (when sp-wrap-show-possible-pairs (overlay-put oend 'after-string (mapconcat (lambda (x) (if sp-highlight-wrap-overlay (concat - (propertize (plist-get x :open) 'face 'sp-wrap-overlay-opening-pair) - (propertize (plist-get x :close) 'face 'sp-wrap-overlay-closing-pair)) + (propertize + (plist-get x :open) 'face + 'sp-wrap-overlay-opening-pair) + (propertize + (plist-get x :close) + 'face 'sp-wrap-overlay-closing-pair)) (concat (plist-get x :open) (plist-get x :close)))) opening-pairs " "))) (when (equal (sp--get-overlay-text obeg) open) - (sp-wrap--finalize :open open close)))) + (sp-wrap--finalize :open (or open-real open) close)))) ((and close (= 1 (length closing-pairs))) (-let (((&plist :open open :close close) close)) (when (equal (sp--get-overlay-text obeg) close) @@ -3030,6 +3587,92 @@ programatically. Use `sp-wrap-with-pair' instead." (t (sp-wrap-cancel))))) +(defun sp--escape-region (chars-to-escape beg end) + "Escape instances of CHARS-TO-ESCAPE between BEG and END. + +Return non-nil if at least one escaping was performed." + (save-excursion + (goto-char beg) + (let ((pattern (regexp-opt chars-to-escape)) + (end-marker (set-marker (make-marker) end)) + (re nil)) + (while (re-search-forward pattern end-marker t) + (setq re t) + (save-excursion + (goto-char (match-beginning 0)) + (insert sp-escape-char))) + re))) + +;; TODO: refactor the rewrap-sexp dependent parts out so that this +;; function has less dependencies on the action +;; TODO: add mode-dependent escape/unescape actions? +(defun sp-escape-wrapped-region (id action _context) + "Escape quotes and special chars when a region is (re)wrapped." + (when (and sp-escape-wrapped-region + (memq action '(wrap rewrap-sexp))) + (sp-get sp-last-wrapped-region + (let* ((parent-delim (save-excursion + (goto-char :beg) + (sp-get (sp-get-string) + (cond + ((and (< :beg (point)) + (< (point) :end)) + :op) + ((eq action 'rewrap-sexp) + (plist-get sp-handler-context :parent))))))) + (cond + ((equal parent-delim id) + (sp--escape-region (list id sp-escape-char) :beg :end)) + (parent-delim + (sp--escape-region (list id) :beg-in :end-in)) + (t + (sp--escape-region (list id sp-escape-char) :beg-in :end-in))))))) + +(defun sp-escape-quotes-after-insert (id action context) + "Escape quotes inserted via `sp-insert-pair'." + (when (and sp-escape-quotes-after-insert + (eq action 'insert) + ;; we test not being inside string because if we were + ;; before inserting the "" pair it is now split into two + ;; -> which moves us outside the pair + (not (eq context 'string)) + ;; the inserted character must have string syntax, otherwise no "context" flip happens + (eq (char-syntax (aref id 0)) ?\")) + (let ((open id) + (close (sp-get-pair id :close))) + (sp--escape-region (list open close) + (- (point) (length open)) + (+ (point) (length close)))))) + +(defun sp--buffer-is-string-balanced-p () + "Check if the buffer is string-balanced. + +A string-balanced buffer is one where where is no unclosed +string, that is, the string state at the end of the buffer is +\"closed\"." + (save-excursion + (save-restriction + (widen) + (goto-char (point-max)) + (let ((syntax (sp--syntax-ppss))) + (or (< (car syntax) 0) + (nth 3 syntax)))))) + +(defun sp-escape-open-delimiter () + "Escape just inserted opening pair if `sp-insert-pair' was skipped. + +This is useful for escaping of \" inside strings when its pairing +is disabled. This way, we can control autoescape and closing +delimiter insertion separately." + (-when-let (open (plist-get (sp--pair-to-insert 'escape) :open)) + (when (and (sp--do-action-p open 'escape) + sp-point-inside-string + ;; do not escape if we are looking at a closing + ;; delimiter, that means we closed an opened string, + ;; most likely. + (sp--buffer-is-string-balanced-p)) + (sp--escape-region (list open) (- (point) (length open)) (point))))) + ;; kept to not break people's config... remove later (defun sp-match-sgml-tags (tag) "Split the html tag TAG at the first space and return its name." @@ -3039,6 +3682,7 @@ programatically. Use `sp-wrap-with-pair' instead." (make-obsolete 'sp-match-sgml-tags "do not use this function as the tag system has been removed." "2015-02-07") (defun sp--is-number-cons (c) + "Return non-nil if C is a cons cell with numbers at `car' and `cdr'." (and (consp c) (numberp (car c)) (numberp (cdr c)))) ;; TODO: more research is needed @@ -3077,12 +3721,10 @@ include separate pair node." (append (list nil second-action nil first-action) previous-undo-actions))))) -(defun sp--string-empty-p (delimeter) - "Return t if point is inside an empty string." - (and (equal (char-after (1+ (point))) delimeter) - (equal (char-after (- (point) 2)) delimeter))) - -(defun sp--all-pairs-to-insert (&optional looking-fn) +;; TODO: remove ACTION argument and make the selection process more +;; unified (see also sp--pair-to-wrap which depends on buffer state +;; among other things) +(defun sp--all-pairs-to-insert (&optional looking-fn action) "Return all pairs that can be inserted at point. Return nil if such pair does not exist. @@ -3091,11 +3733,17 @@ Pairs inserted using a trigger have higher priority over pairs without a trigger and only one or the other list is returned. In other words, if any pair can be inserted using a trigger, only -pairs insertable by trigger are returned." +pairs insertable by trigger are returned. + +ACTION is an implementation detail. Usually it has the value +'insert when we determine pairs to insert. On repeated wrapping +however we pass the value 'wrap. This will be refactored away in +the upcoming version." (setq looking-fn (or looking-fn 'sp--looking-back-p)) + (setq action (or action 'insert)) (let ((working-pairs ;; TODO: abstract this into a new "sp--get-..." hierarchy - (--filter (sp--do-action-p (plist-get it :open) 'insert) sp-local-pairs))) + (--filter (sp--do-action-p (plist-get it :open) action) sp-local-pairs))) (-if-let (trigs (--filter (and (plist-get it :trigger) (funcall looking-fn (sp--strict-regexp-quote (plist-get it :trigger)))) working-pairs)) @@ -3118,14 +3766,14 @@ pairs insertable by trigger are returned." (not (and (string-prefix-p (plist-get b :close) (plist-get a :close)) (sp--looking-at-p (plist-get b :close)))))))) -(defun sp--pair-to-insert () +(defun sp--pair-to-insert (&optional action) "Return pair that can be inserted at point. Return nil if such pair does not exist. If more triggers or opening pairs are possible select the shortest one." - (-when-let ((property . pairs) (sp--all-pairs-to-insert)) + (-when-let ((property . pairs) (sp--all-pairs-to-insert nil action)) (car (--sort (sp--pair-to-insert-comparator property it other) pairs)))) (defun sp--longest-prefix-to-insert () @@ -3179,93 +3827,113 @@ setting `sp-autoinsert-pair' to nil. You can globally disable insertion of closing pair if point is followed by the matching opening pair. It is disabled by default." - (-let* ((active-pair (unwind-protect - ;; This fake insertion manufactures proper - ;; context for the tests below... in effect - ;; we must make it look as if the user - ;; typed in the opening part themselves - ;; TODO: it is duplicated in the test - ;; below, maybe it wouldn't hurt to - ;; restructure this function a bit - (progn - (when pair (insert pair)) - (sp--pair-to-insert)) - (when pair (delete-char (- (length pair)))))) - ((open-pair close-pair trig) (sp--insert-pair-get-pair-info active-pair))) - (if (not (unwind-protect - (progn - (when pair (insert pair)) - ;; TODO: all these tests must go into `sp--pair-to-insert' - (and sp-autoinsert-pair - active-pair - (if (eq sp-autoskip-closing-pair 'always) - (or (not (equal open-pair close-pair)) - (not (sp-skip-closing-pair nil t))) - t) - (sp--do-action-p open-pair 'insert t) - (if sp-autoinsert-quote-if-followed-by-closing-pair t - (if (and (eq (char-syntax (preceding-char)) ?\") - ;; this is called *after* the character is - ;; inserted. Therefore, if we are not in string, it - ;; must have been closed just now - (not (sp-point-in-string))) - (let ((pattern (sp--get-closing-regexp))) - ;; If we simply insert closing ", we also - ;; don't want to escape it. Therefore, we - ;; need to set `sp-last-operation' - ;; accordingly to be checked in - ;; `self-insert-command' advice. - (if (sp--looking-at pattern) - (progn (setq sp-last-operation 'sp-self-insert-no-escape) nil) - t)) - t)) - ;; was sp-autoinsert-if-followed-by-same - (or (not (sp--get-active-overlay 'pair)) - (not (sp--looking-at (sp--strict-regexp-quote open-pair))) - (and (equal open-pair close-pair) - (eq sp-last-operation 'sp-insert-pair) - (save-excursion - (backward-char (length trig)) - (sp--looking-back (sp--strict-regexp-quote open-pair)))) - (not (equal open-pair close-pair))))) - (when pair (delete-char (- (length pair)))))) - ;; if this pair could not be inserted, we try the procedure - ;; again with this pair removed from sp-pair-list to give - ;; chance to other pairs sharing a common suffix (for - ;; example \[ and [) - (let ((new-sp-pair-list (--remove (equal (car it) open-pair) sp-pair-list))) - (when (> (length sp-pair-list) (length new-sp-pair-list)) - (let ((sp-pair-list new-sp-pair-list)) - (sp-insert-pair)))) - ;; setup the delayed insertion here. - (if (sp-get-pair open-pair :when-cond) - (progn - (setq sp-delayed-pair (cons open-pair (- (point) (length open-pair)))) - (setq sp-last-operation 'sp-insert-pair-delayed)) - (unless pair (delete-char (- (length trig)))) - (insert open-pair) - (sp--run-hook-with-args open-pair :pre-handlers 'insert) - (--when-let (sp--pair-to-uninsert) - (let ((cl (plist-get it :close))) - (when (and (sp--looking-at-p (sp--strict-regexp-quote cl)) - (not (string-prefix-p cl close-pair))) - (delete-char (length cl))))) - (insert close-pair) - (backward-char (length close-pair)) - (sp--pair-overlay-create (- (point) (length open-pair)) - (+ (point) (length close-pair)) - open-pair) - (when sp-undo-pairs-separately - (sp--split-last-insertion-undo (+ (length open-pair) (length close-pair))) - ;; TODO: abc\{abc\} undo undo \{asd\} . next undo removes the - ;; entire \{asd\} if we do not insert two nils here. - ;; Normally, repeated nils are ignored so it shouldn't - ;; matter. It would still be useful to inspect further. - (push nil buffer-undo-list) - (push nil buffer-undo-list)) - (sp--run-hook-with-args open-pair :post-handlers 'insert) - (setq sp-last-inserted-pair open-pair) - (setq sp-last-operation 'sp-insert-pair))))) + (sp--with-case-sensitive + (catch 'done + (-let* ((active-pair (unwind-protect + ;; This fake insertion manufactures proper + ;; context for the tests below... in effect + ;; we must make it look as if the user + ;; typed in the opening part themselves + ;; TODO: it is duplicated in the test + ;; below, maybe it wouldn't hurt to + ;; restructure this function a bit + (progn + (when pair (insert pair)) + (sp--pair-to-insert)) + (when pair (delete-char (- (length pair)))))) + ((open-pair close-pair trig) (sp--insert-pair-get-pair-info active-pair))) + ;; We are not looking at a closing delimiter which might mean we + ;; are in an already existing sexp. If the to-be-inserted pair + ;; has a prefix which is also a pair we migth be extending the + ;; opener of a sexp with this opener. In which case we should + ;; probably rewrap. + (unless (sp--looking-at-p (sp--get-closing-regexp)) + (when (and open-pair + (= 1 (- (point) sp-pre-command-point))) + (-when-let (prefix-pair (sp-get-pair (substring open-pair 0 -1))) + (let ((last-char-of-open-pair (substring open-pair -1))) + (unwind-protect + (progn + (delete-char -1) + (--when-let (sp-get-thing t) + (save-excursion + (sp-get it + (delete-region :end-in :end) + (goto-char :end-in) + (insert close-pair))) + (throw 'done t))) + (insert last-char-of-open-pair)))))) + (if (not (unwind-protect + (progn + (when pair (insert pair)) + ;; TODO: all these tests must go into `sp--pair-to-insert' + (and sp-autoinsert-pair + active-pair + (if (memq sp-autoskip-closing-pair '(always always-end)) + (or (not (equal open-pair close-pair)) + (not (sp-skip-closing-pair nil t))) + t) + (sp--do-action-p open-pair 'insert t) + ;; was sp-autoinsert-if-followed-by-same + (or (not (sp--get-active-overlay 'pair)) + (not (sp--looking-at (sp--strict-regexp-quote open-pair))) + (and (equal open-pair close-pair) + (eq sp-last-operation 'sp-insert-pair) + (save-excursion + (backward-char (length trig)) + (sp--looking-back (sp--strict-regexp-quote open-pair)))) + (not (equal open-pair close-pair))))) + (when pair (delete-char (- (length pair)))))) + ;; if this pair could not be inserted, we try the procedure + ;; again with this pair removed from sp-pair-list to give + ;; chance to other pairs sharing a common suffix (for + ;; example \[ and [) + (let ((new-sp-pair-list (--remove (equal (car it) open-pair) sp-pair-list)) + (new-sp-local-pairs (--remove (equal (plist-get it :open) open-pair) sp-local-pairs))) + (when (> (length sp-pair-list) (length new-sp-pair-list)) + (let ((sp-pair-list new-sp-pair-list) + (sp-local-pairs new-sp-local-pairs)) + (sp-insert-pair)))) + ;; setup the delayed insertion here. + (if (sp-get-pair open-pair :when-cond) + (progn + (setq sp-delayed-pair (cons open-pair (- (point) (length open-pair)))) + (setq sp-last-operation 'sp-insert-pair-delayed)) + (unless pair (delete-char (- (length trig)))) + (insert open-pair) + (sp--run-hook-with-args open-pair :pre-handlers 'insert) + ;; The re-binding of these dynamic variables is a hack to + ;; combat the similar rebinding in the branch above where + ;; we retry `sp-insert-pair' with some pairs removed. + ;; This however causes them to be uninserted improperly, + ;; so for this one operation we need to restore the state + ;; to the "full" pair list. TODO: in the future we might + ;; want to pass the state around explicitly so we have + ;; better control. + (--when-let (let ((sp-pair-list (sp-state-pair-list sp-state)) + (sp-local-pairs (sp-state-local-pairs sp-state))) + (sp--pair-to-uninsert)) + (let ((cl (plist-get it :close))) + (when (and (sp--looking-at-p (sp--strict-regexp-quote cl)) + (> (- (length close-pair) (length cl)) 0)) + (delete-char (length cl))))) + (insert close-pair) + (backward-char (length close-pair)) + (sp--pair-overlay-create (- (point) (length open-pair)) + (+ (point) (length close-pair)) + open-pair) + (when sp-undo-pairs-separately + (sp--split-last-insertion-undo (+ (length open-pair) (length close-pair))) + ;; TODO: abc\{abc\} undo undo \{asd\} . next undo removes the + ;; entire \{asd\} if we do not insert two nils here. + ;; Normally, repeated nils are ignored so it shouldn't + ;; matter. It would still be useful to inspect further. + (push nil buffer-undo-list) + (push nil buffer-undo-list)) + (sp--run-hook-with-args open-pair :post-handlers 'insert) + (setq sp-last-inserted-pair open-pair) + (setf (sp-state-delayed-hook sp-state) (cons :next open-pair)) + (setq sp-last-operation 'sp-insert-pair))))))) (defun sp--wrap-repeat-last (active-pair) "If the last operation was a wrap and `sp-wrap-repeat-last' is @@ -3316,12 +3984,14 @@ active region." (-map 'car) (--any? (string-match-p (regexp-quote char) it)))) -(defun sp--char-is-part-of-closing (char) - "Return non-nil if CHAR is part of a pair delimiter of length 1." - (->> (sp--get-pair-list) - (--filter (= 1 (length (cdr it)))) - (-map 'cdr) - (--any? (string-match-p (regexp-quote char) it)))) +(defun sp--char-is-part-of-closing (char &optional pair-list) + "Return non-nil if CHAR is part of a pair delimiter of length 1. +Specifically, return the pair for which CHAR is the closing +delimiter." + (let ((regexp (regexp-quote char))) + (->> (or pair-list (sp--get-pair-list)) + (--filter (= 1 (length (cdr it)))) + (--find (string-match-p regexp (cdr it)))))) ;; TODO: this only supports single-char delimiters. Maybe it should ;; that that way. @@ -3346,94 +4016,131 @@ Additionally, this behaviour can be selectively disabled for specific pairs by removing their \"autoskip\" action. You can achieve this by using `sp-pair' or `sp-local-pair' with \":actions '(:rem autoskip)\"." - (when (or (and (eq sp-autoskip-closing-pair t) - sp-pair-overlay-list - (sp--get-active-overlay 'pair)) - (memq sp-autoskip-closing-pair '(always always-end))) - ;; these two are pretty hackish ~_~ - (cl-labels ((get-sexp - () - (delete-char -1) - (insert " ") - (prog1 (sp-get-sexp) - (delete-char -1) - (insert last))) - (get-enclosing-sexp - () - (delete-char -1) - (insert " ") - (prog1 (sp-get-enclosing-sexp) - (delete-char -1) - (insert last)))) - (let ((last (or last (sp--single-key-description last-command-event)))) - (-when-let (active-sexp - (cond - ((-when-let* ((ov (sp--get-active-overlay 'pair)) - (op (overlay-get ov 'pair-id)) - (cl (cdr (assoc op sp-pair-list)))) - ;; if the sexp is active, we are inside it. - (when (and (= 1 (length op)) - (equal last cl)) - (list :beg (overlay-start ov) - :end (overlay-end ov) - :op op - :cl cl - :prefix "" - :suffix "")))) - ((sp--char-is-part-of-stringlike last) - ;; a part of closing delimiter is typed. There are four - ;; options now: - ;; - we are inside the sexp, at its end - ;; - we are inside the sexp, somewhere in the middle - ;; - we are outside, in front of a sexp - ;; - we are outside, somewhere between sexps + (sp--with-case-sensitive + (when (or (and (eq sp-autoskip-closing-pair t) + sp-pair-overlay-list + (sp--get-active-overlay 'pair)) + (memq sp-autoskip-closing-pair '(always always-end))) + ;; TODO: ugly hack to override 'navigate with 'autoskip. Each of + ;; these submodules should set-up their own environment somehow + ;; and thread it through the entire computation + (cl-letf (((symbol-function 'sp--get-allowed-stringlike-list) + (lambda () + (--filter (and (sp--do-action-p (car it) 'autoskip) + (equal (car it) (cdr it))) sp-pair-list)))) + ;; these two are pretty hackish ~_~ + (cl-labels ((get-sexp + (last) + (delete-char -1) + (insert " ") + (prog1 (sp-get-sexp) + (delete-char -1) + (insert last))) + (get-enclosing-sexp + (last) + (delete-char -1) + (insert " ") + (prog1 (sp-get-enclosing-sexp) + (delete-char -1) + (insert last)))) + (let ((last (or last (sp--single-key-description last-command-event)))) + (-if-let (active-sexp (cond - ((and (sp--looking-at (sp--get-stringlike-regexp)) - (not (sp--skip-match-p (match-string-no-properties 0) - (match-beginning 0) - (match-end 0)))) - ;; if we're looking at the delimiter, and it is valid in - ;; current context, get the sexp. - (get-sexp)) - ;; here comes the feature when we're somewhere in the - ;; middle of the sexp (or outside), if ever supported. - )) - ((sp--char-is-part-of-closing last) - (cond - ((and (sp--looking-at (sp--get-closing-regexp)) - (not (sp--skip-match-p (match-string-no-properties 0) - (match-beginning 0) - (match-end 0)))) - (get-sexp)) - ((eq sp-autoskip-closing-pair 'always) - (get-enclosing-sexp)))))) - (when (and active-sexp - (equal (sp-get active-sexp :cl) last) - (sp--do-action-p (sp-get active-sexp :op) 'autoskip)) - (-when-let (re (cond - ((= (point) (sp-get active-sexp :beg)) - ;; we are in front of a string-like sexp - (when sp-autoskip-opening-pair - (if test-only t - (delete-char -1) - (forward-char) - (setq sp-last-operation 'sp-skip-closing-pair)))) - ((= (point) (sp-get active-sexp :end-in)) - (if test-only t - (delete-char 1) - (setq sp-last-operation 'sp-skip-closing-pair))) - ((sp-get active-sexp - (and (> (point) :beg-in) - (< (point) :end-in))) - (if test-only t - (delete-char -1) - (sp-up-sexp))))) - (unless (or test-only - sp-buffer-modified-p) - (set-buffer-modified-p nil)) - (unless test-only - (sp--run-hook-with-args (sp-get active-sexp :op) :post-handlers 'skip-closing-pair)) - re))))))) + ((-when-let* ((ov (sp--get-active-overlay 'pair)) + (op (overlay-get ov 'pair-id)) + (cl (cdr (assoc op sp-pair-list)))) + ;; if the sexp is active, we are inside it. + (when (and (= 1 (length op)) + (equal last cl)) + (list :beg (overlay-start ov) + :end (overlay-end ov) + :op op + :cl cl + :prefix "" + :suffix "")))) + ((sp--char-is-part-of-stringlike last) + ;; a part of closing delimiter is typed. There are four + ;; options now: + ;; - we are inside the sexp, at its end + ;; - we are inside the sexp, somewhere in the middle + ;; - we are outside, in front of a sexp + ;; - we are outside, somewhere between sexps + (cond + ((and (sp--looking-at (sp--get-stringlike-regexp)) + (not (sp--skip-match-p (match-string-no-properties 0) + (match-beginning 0) + (match-end 0)))) + ;; if we're looking at the delimiter, and it is valid in + ;; current context, get the sexp. + (get-sexp last)) + ;; here comes the feature when we're somewhere in the + ;; middle of the sexp (or outside), if ever supported. + )) + ((sp--char-is-part-of-closing last) + (cond + ((and (sp--looking-at (sp--get-closing-regexp)) + (not (sp--skip-match-p (match-string-no-properties 0) + (match-beginning 0) + (match-end 0)))) + (get-sexp last)) + ((eq sp-autoskip-closing-pair 'always) + (get-enclosing-sexp last)))))) + (if (and active-sexp + (equal (sp-get active-sexp :cl) last) + (sp--do-action-p (sp-get active-sexp :op) 'autoskip) + ;; if the point is inside string and preceded + ;; by an odd number of `sp-escape-char's, we + ;; should not skip as that would leave the + ;; string broken. + (or (not (sp-point-in-string)) + (not (sp-char-is-escaped-p (1- (point)))))) + (-when-let (re (cond + ((= (point) (sp-get active-sexp :beg)) + ;; we are in front of a string-like sexp + (when sp-autoskip-opening-pair + (if test-only t + (delete-char -1) + (forward-char) + (setq sp-last-operation 'sp-skip-closing-pair)))) + ((= (point) (sp-get active-sexp :end-in)) + (if test-only t + (delete-char 1) + (setq sp-last-operation 'sp-skip-closing-pair))) + ((sp-get active-sexp + (and (> (point) :beg-in) + (< (point) :end-in))) + (if test-only t + (delete-char -1) + (sp-up-sexp nil t))))) + (unless (or test-only + sp-buffer-modified-p) + (set-buffer-modified-p nil)) + (unless test-only + (sp--run-hook-with-args (sp-get active-sexp :op) :post-handlers 'skip-closing-pair)) + re) + ;; if we can't skip and are in strict mode we must not + ;; insert anything if it is a closing character + (sp--inhibit-insertion-of-closing-delim last)) + (sp--inhibit-insertion-of-closing-delim last)))))))) + +(defun sp--inhibit-insertion-of-closing-delim (last) + "Inhibit insertion of closing delimiter in `smartparens-strict-mode'. + +If we are not inserting inside string or a comment, and the LAST +inserted character is closing delimiter for a pair that performs +autoskip, and we can not jump out of its enclosing sexp (i.e. it +does not match), we are not allowed to insert it literally +because it would break the balance; so we delete the +just-inserted character." + (when (and smartparens-strict-mode + (-when-let (pair (sp--char-is-part-of-closing + last (sp--get-allowed-pair-list))) + (memq 'autoskip (sp-get-pair (car pair) :actions))) + (not (sp-point-in-string-or-comment))) + (delete-char -1) + (set-buffer-modified-p sp-buffer-modified-p) + (sp-message :cant-insert-closing-delimiter) + nil)) (defun sp-delete-pair (&optional arg) "Automatically delete opening or closing pair, or both, depending on @@ -3457,71 +4164,79 @@ is remove the just added wrapping." ;; only activate if argument is 1 (this is 0-th argument of the ;; delete-backward-char), otherwise the user wants to delete ;; multiple character, so let him do that - (when (and (= arg 1) - smartparens-mode) - (if (and sp-autodelete-wrap - (eq sp-last-operation 'sp-wrap-region)) + (sp--with-case-sensitive + (when (and (= arg 1) + smartparens-mode) + (if (and sp-autodelete-wrap + (eq sp-last-operation 'sp-wrap-region)) + (let ((p (point)) + (b (sp-get sp-last-wrapped-region :beg)) + (e (sp-get sp-last-wrapped-region :end)) + (o (sp-get sp-last-wrapped-region :op-l)) + (c (sp-get sp-last-wrapped-region :cl-l))) + ;; if the last operation was `sp-wrap-region', and we are at + ;; the position of either opening or closing pair, delete the + ;; just-inserted pair + (when (or (= p (+ b o)) + (= p e)) + (insert "x") ;dummy char to account for the regularly deleted one + (save-excursion + (goto-char e) + (delete-char (- c)) + (goto-char b) + (delete-char o)) + (setq sp-last-operation 'sp-delete-pair-wrap))) (let ((p (point)) - (b (sp-get sp-last-wrapped-region :beg)) - (e (sp-get sp-last-wrapped-region :end)) - (o (sp-get sp-last-wrapped-region :op-l)) - (c (sp-get sp-last-wrapped-region :cl-l))) - ;; if the last operation was `sp-wrap-region', and we are at - ;; the position of either opening or closing pair, delete the - ;; just-inserted pair - (when (or (= p (+ b o)) - (= p e)) - (insert "x") ;dummy char to account for the regularly deleted one - (save-excursion - (goto-char e) - (delete-char (- c)) - (goto-char b) - (delete-char o)) - (setq sp-last-operation 'sp-delete-pair-wrap))) - (let ((p (point)) - (inside-pair (--first (and (sp--looking-back (sp--strict-regexp-quote (car it))) - (sp--looking-at (concat "[ \n\t]*" (sp--strict-regexp-quote (cdr it))))) - sp-pair-list)) - (behind-pair (--first (sp--looking-back (sp--strict-regexp-quote (cdr it))) sp-pair-list)) - (opening-pair (--first (sp--looking-back (sp--strict-regexp-quote (car it))) sp-pair-list))) + (inside-pair (--first (and (sp--looking-back (sp--strict-regexp-quote (car it))) + (sp--looking-at (concat "[ \n\t]*" (sp--strict-regexp-quote (cdr it))))) + sp-pair-list)) + (behind-pair (--first (sp--looking-back (sp--strict-regexp-quote (cdr it))) sp-pair-list)) + (opening-pair (--first (sp--looking-back (sp--strict-regexp-quote (car it))) sp-pair-list))) - (cond - ;; we're just before the closing quote of a string. If there - ;; is an opening or closing pair behind the point, remove - ;; it. This is only really relevant if the pair ends in the - ;; same character as string quote. We almost never want to - ;; delete it as an autopair (it would "open up the string"). - ;; So, word\"|" and should produce word\|" or - ;; word|" (if \" is autopair) instead of word\|. - ((and (sp-point-in-string) - (not (sp-point-in-string (1+ p))) - (sp-point-in-string (1- p))) ;; the string isn't empty - (cond ;; oh, you ugly duplication :/ + (cond + ;; we're just before the closing quote of a string. If there + ;; is an opening or closing pair behind the point, remove + ;; it. This is only really relevant if the pair ends in the + ;; same character as string quote. We almost never want to + ;; delete it as an autopair (it would "open up the string"). + ;; So, word\"|" and should produce word\|" or + ;; word|" (if \" is autopair) instead of word\|. + ((and (sp-point-in-string) + (not (sp-point-in-string (1+ p))) + (sp-point-in-string (1- p))) ;; the string isn't empty + (cond ;; oh, you ugly duplication :/ + ((and behind-pair sp-autodelete-closing-pair) + (delete-char (- (1- (length (car behind-pair))))) + (setq sp-last-operation 'sp-delete-pair-closing)) + ((and opening-pair sp-autodelete-opening-pair) + (delete-char (- (1- (length (car opening-pair))))) + (setq sp-last-operation 'sp-delete-pair-opening)))) + ;; we're inside a pair + ((and inside-pair sp-autodelete-pair) + (let* ((beg (save-excursion + (search-backward (car inside-pair)))) + (end (save-excursion + (search-forward (cdr inside-pair)))) + (cs (sp--get-context p)) + (ce (sp--get-context end)) + (current-sexp (sp-get-sexp))) + (when (and (or (not (eq cs 'comment)) ;; a => b <=> ~a v b + (eq ce 'comment)) + (eq beg (sp-get current-sexp :beg)) + (eq end (sp-get current-sexp :end)) + (equal (sp-get current-sexp :op) (car inside-pair)) + (equal (sp-get current-sexp :cl) (cdr inside-pair))) + (delete-char (- end p)) + (delete-char (- (1- (length (car inside-pair))))) + (setq sp-last-operation 'sp-delete-pair)))) + ;; we're behind a closing pair ((and behind-pair sp-autodelete-closing-pair) - (delete-char (- (1- (length (car behind-pair))))) + (delete-char (- (1- (length (cdr behind-pair))))) (setq sp-last-operation 'sp-delete-pair-closing)) + ;; we're behind an opening pair and there's no closing pair ((and opening-pair sp-autodelete-opening-pair) (delete-char (- (1- (length (car opening-pair))))) - (setq sp-last-operation 'sp-delete-pair-opening)))) - ;; we're inside a pair - ((and inside-pair sp-autodelete-pair) - (let* ((end (save-excursion - (search-forward (cdr inside-pair)))) - (cs (sp--get-context p)) - (ce (sp--get-context end))) - (when (or (not (eq cs 'comment)) ;; a => b <=> ~a v b - (eq ce 'comment)) - (delete-char (- end p)) - (delete-char (- (1- (length (car inside-pair))))) - (setq sp-last-operation 'sp-delete-pair)))) - ;; we're behind a closing pair - ((and behind-pair sp-autodelete-closing-pair) - (delete-char (- (1- (length (cdr behind-pair))))) - (setq sp-last-operation 'sp-delete-pair-closing)) - ;; we're behind an opening pair and there's no closing pair - ((and opening-pair sp-autodelete-opening-pair) - (delete-char (- (1- (length (car opening-pair))))) - (setq sp-last-operation 'sp-delete-pair-opening))))))) + (setq sp-last-operation 'sp-delete-pair-opening)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3529,12 +4244,12 @@ is remove the just added wrapping." (defun sp--looking-at (regexp) "Like `looking-at', but always case sensitive." - (let ((case-fold-search nil)) + (sp--with-case-sensitive (looking-at regexp))) (defun sp--looking-at-p (regexp) "Like `looking-at-p', but always case sensitive." - (let ((case-fold-search nil)) + (sp--with-case-sensitive (looking-at-p regexp))) (defun sp--looking-back (regexp &optional limit not-greedy) @@ -3546,23 +4261,26 @@ backward. If LIMIT is nil, default to `sp-max-pair-length'. If optional argument NON-GREEDY is t search for any matching sequence, not necessarily the longest possible." (setq limit (or limit sp-max-pair-length)) - (let ((case-fold-search nil) - (from (max 1 (- (point) limit))) - (to (point)) - (greedy (not not-greedy)) - has-match) - (set-match-data '(0 0)) - (if greedy - (save-excursion - (goto-char from) - (while (and (not has-match) (< (point) to)) - (sp--looking-at regexp) - (if (= (match-end 0) to) - (setq has-match t) - (forward-char 1))) + (sp--with-case-sensitive + (let ((from (max 1 (- (point) limit))) + (to (point)) + (greedy (not not-greedy)) has-match) - (save-excursion - (not (null (search-backward-regexp (concat "\\(?:" regexp "\\)\\=") from t))))))) + (if greedy + (save-excursion + (goto-char from) + (save-match-data + (while (and (not has-match) (< (point) to)) + ;; don't use looking-at because we can't limit that search + (if (and (save-excursion (re-search-forward regexp to t)) + (= (match-end 0) to)) + (setq has-match (match-data)) + (forward-char 1)))) + (when has-match + (set-match-data has-match) + t)) + (save-excursion + (not (null (search-backward-regexp (concat "\\(?:" regexp "\\)\\=") from t)))))))) (defun sp--looking-back-p (regexp &optional limit not-greedy) "Same as `sp--looking-back' but do not change the match data." @@ -3579,34 +4297,67 @@ This is an internal function. Only use this for searching for pairs!" (setq count (or count 1)) (setq bound (or (sp--get-backward-bound) bound)) - (let ((case-fold-search nil) r) - (while (> count 0) - (when (search-backward-regexp regexp bound noerror) - (goto-char (match-end 0)) - (sp--looking-back regexp) - (setq r (goto-char (match-beginning 0)))) - (setq count (1- count))) - r)) + (sp--with-case-sensitive + (let (r) + (while (> count 0) + (when (search-backward-regexp regexp bound noerror) + (goto-char (match-end 0)) + (if (sp--looking-back regexp) + (setq r (goto-char (match-beginning 0))) + (if noerror nil (error "Search failed: %s" regexp)))) + (setq count (1- count))) + r))) (defun sp--search-forward-regexp (regexp &optional bound noerror count) "Just like `search-forward-regexp', but always case sensitive." (setq bound (or (sp--get-forward-bound) bound)) - (let ((case-fold-search nil)) + (sp--with-case-sensitive (search-forward-regexp regexp bound noerror count))) -(defun sp-get-quoted-string-bounds () - "Return the bounds of the string around point. +(defun sp--search-forward-in-context (regexp &optional bound noerror count) + "Just like `sp--search-forward-regexp' but only accept results in same context. + +The context at point is considered the reference context." + (let ((context (sp--get-context)) + (re)) + (--dotimes (or count 1) + (save-excursion + (while (and (setq re (sp--search-forward-regexp regexp bound noerror)) + (not (eq (sp--get-context) context))))) + (when re (goto-char re))) + re)) + +(defun sp--search-backward-in-context (regexp &optional bound noerror count) + "Just like `sp--search-backward-regexp' but only accept results in same context. + +The context at point is considered the reference context." + (let ((context (sp--get-context)) + (re)) + (--dotimes (or count 1) + (save-excursion + (while (and (setq re (sp--search-backward-regexp regexp bound noerror)) + (not (eq (sp--get-context) context)))) + (when re (goto-char re)))) + re)) + +(defun sp-get-quoted-string-bounds (&optional point) + "Return the bounds of the string around POINT. + +POINT defaults to `point'. If the point is not inside a quoted string, return nil." - (let ((parse-data (syntax-ppss))) - (when (nth 3 parse-data) - (let* ((open (nth 8 parse-data)) - (close (save-excursion - (parse-partial-sexp - (point) (point-max) - nil nil parse-data 'syntax-table) - (point)))) - (cons open close))))) + (setq point (or point (point))) + (save-excursion + (goto-char point) + (let ((parse-data (syntax-ppss))) + (when (nth 3 parse-data) + (let* ((open (nth 8 parse-data)) + (close (save-excursion + (parse-partial-sexp + (point) (point-max) + nil nil parse-data 'syntax-table) + (point)))) + (cons open close)))))) ;; TODO: the repeated conditions are ugly, refactor this! (defun sp-get-comment-bounds () @@ -3614,17 +4365,20 @@ If the point is not inside a quoted string, return nil." (when (or (sp-point-in-comment) (looking-at "[[:space:]]+\\s<")) (let ((open (save-excursion + (--when-let (nth 8 (sp--syntax-ppss)) + (goto-char it)) (while (and (not (bobp)) - (or (sp-point-in-comment) - (save-excursion + (or (when (sp-point-in-comment) (backward-char 1) - (looking-at "[[:space:]]+\\s<")))) - (backward-char 1)) - (when (not (or (bobp) - (or (sp-point-in-comment) - (save-excursion - (backward-char 1) - (looking-at "[[:space:]]+\\s<"))))) + t) + (when (save-excursion + (beginning-of-line) + (looking-at "^[[:space:]]+\\s<")) + (when (>= (forward-line -1) 0) + (end-of-line)) + t)))) + ;; this means we got here by `sp-point-in-comment' condition + (unless (and (bobp) (sp-point-in-comment)) (forward-char)) (point))) (close (save-excursion @@ -3634,18 +4388,18 @@ If the point is not inside a quoted string, return nil." (forward-char 1)) (let ((pp (1- (point)))) (when (not (or (eobp) - (sp-point-in-comment) - (looking-at "[[:space:]]+\\s<") - (and (eq (char-syntax - (char-after pp)) ?>) - (not (eq (char-after pp) ?\n))) - (/= (logand - (lsh 1 18) - (car (syntax-after pp))) 0) - (/= (logand - (lsh 1 19) - (car (syntax-after pp))) 0))) - (backward-char 1))) + (sp-point-in-comment) + (looking-at "[[:space:]]+\\s<") + (and (eq (char-syntax + (char-after pp)) ?>) + (not (eq (char-after pp) ?\n))) + (/= (logand + (lsh 1 18) + (car (syntax-after pp))) 0) + (/= (logand + (lsh 1 19) + (car (syntax-after pp))) 0))) + (backward-char 1))) (point)))) (cons open close)))) @@ -3685,13 +4439,22 @@ the test functions as keyword arguments to speed up the lookup." "Test the last match using `sp--skip-match-p'. The form should be a function call that sets the match data." (declare (debug (form))) - `(and ,form - (not (sp--skip-match-p - (match-string 0) - (match-beginning 0) - (match-end 0))))) - -(defun sp--elisp-skip-match (ms mb me) + (let ((match (make-symbol "match")) + (pair-skip (make-symbol "pair-skip"))) + `(and ,form + (let* ((,match (match-string 0)) + (,pair-skip (or (sp-get-pair ,match :skip-match) + (sp-get-pair (car (--first + (equal (cdr it) ,match) + sp-pair-list)) + :skip-match)))) + (not (sp--skip-match-p + ,match + (match-beginning 0) + (match-end 0) + :pair-skip ,pair-skip)))))) + +(defun sp--elisp-skip-match (ms mb _me) "Function used to test for escapes in lisp modes. Non-nil return value means to skip the result." @@ -3709,7 +4472,7 @@ Non-nil return value means to skip the result." (not (sp--looking-back "\\s_\\?" 2 t)) (not (sp--looking-back "\\sw\\?" 2 t)))))))) -(defun sp--backslash-skip-match (ms mb me) +(defun sp--backslash-skip-match (ms mb _me) (and ms (save-excursion (goto-char mb) @@ -3724,189 +4487,204 @@ Non-nil return value means to skip the result." The expressions considered are those delimited by pairs on `sp-pair-list'." - (save-excursion - (let* ((search-fn (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) - (global-skip-fn (cdr (--first (memq major-mode (car it)) sp-navigate-skip-match))) - (pair-list (sp--get-allowed-pair-list)) - ;; TODO UGLY HACK!!! When the situation is: - ;; ..)|;; comment - ;; the context the point gets is the comment. But if we - ;; are searching backward, that is incorrect, because in - ;; that case we want the context of the closing pair. - ;; Therefore, if the direction is backward, we need to move - ;; one point backward, then test the comment/string thing, - ;; then compute the correct bounds, and then restore the - ;; point so the search will pick up the ) - - ;; However, we need to distinguish the cases where we are - ;; in comment and trying to get out, and when we are in any - ;; context and we jump into string (in that case, we should - ;; report code context!). For example: - ;; "foo"|;bar - ;; or - ;; "foo"|bar - ;; should both report code context - ;; and "|(foo)" should report string context. - - ;; Beware the case when we have a string inside a comment, like - ;; (foo) ;; bar "baz"| qux - ;; In this case we want to report comment context even when - ;; backing into the "" (which however is commented) - - ;; Yet another case is when we are not in a comment but - ;; directly after one and we search backwards, consider: - ;; /* foo bar */| - ;; in C-like language. In this case, we want to report the - ;; context as comment. - - ;; Thanks for being consistent at handling syntax bounds Emacs! - (in-string-or-comment (if back - (let ((in-comment (sp-point-in-comment)) - (in-string (sp-point-in-string))) - (save-excursion - (unless (= (point) (point-min)) - (backward-char) - (cond - (in-comment (and in-comment (sp-point-in-comment))) - ((and (not in-comment) (sp-point-in-comment)) t) - ((or in-comment in-string)))))) - (sp-point-in-string-or-comment))) - (string-bounds (and in-string-or-comment (sp--get-string-or-comment-bounds))) - (fw-bound (if in-string-or-comment (cdr string-bounds) (point-max))) - (bw-bound (if in-string-or-comment (car string-bounds) (point-min))) - s e active-pair forward mb me ms r done - possible-pairs possible-interfering-pairs possible-ops possible-cls) - (while (and (not done) - (sp--search-and-save-match - search-fn - ;; #556 The regexp we use here might exclude or - ;; include extra pairs in case the next match is in - ;; a different context. There's no way to know - ;; beforehand where we land, so we need to consider - ;; *all* pairs in the search and then re-check with - ;; a regexp based on the context of the found pair - (sp--get-allowed-regexp - ;; use all the pairs! - (sp--get-pair-list)) - (if back bw-bound fw-bound) - r mb me ms)) - ;; search for the first opening pair. Here, only consider tags - ;; that are allowed in the current context. - (unless (or (not (save-excursion - (if back - (progn - (goto-char me) - (sp--looking-back-p (sp--get-allowed-regexp))) - (goto-char mb) - (sp--looking-at-p (sp--get-allowed-regexp))))) - (sp--skip-match-p ms mb me :global-skip global-skip-fn)) - ;; if the point originally wasn't inside of a string or comment - ;; but now is, jump out of the string/comment and only search - ;; the code. This ensures that the comments and strings are - ;; skipped if we search inside code. - (if (and (not in-string-or-comment) - (if back - ;; When searching back, the point lands on the - ;; first character of whatever pair we've found - ;; and it is in the proper context, for example - ;; "|(foo)" - (sp-point-in-string-or-comment) - ;; However, when searching forward, the point - ;; lands after the last char of the pair so to get - ;; its context we must back up one character - (sp-point-in-string-or-comment (1- (point))))) - (-if-let (bounds (sp--get-string-or-comment-bounds)) - (let ((jump-to (if back (car bounds) (cdr bounds)))) - (goto-char jump-to) - ;; Can't move out of comment because eob, #427 - (when (eobp) - (setq done t))) - (setq done t)) - (setq done t)))) - (when r - (setq possible-pairs (--filter (or (equal ms (car it)) - (equal ms (cdr it))) - pair-list)) - (setq possible-ops (-map 'car possible-pairs)) - (setq possible-cls (-map 'cdr possible-pairs)) - (setq pair-list (-difference pair-list possible-pairs)) - (setq possible-interfering-pairs pair-list) - (while possible-interfering-pairs - (setq possible-interfering-pairs - (--filter (or (-contains? possible-ops (car it)) - (-contains? possible-cls (cdr it))) - pair-list)) - (setq pair-list (-difference pair-list possible-interfering-pairs)) - (setq possible-ops (append possible-ops (-map 'car possible-interfering-pairs))) - (setq possible-cls (append possible-cls (-map 'cdr possible-interfering-pairs)))) - (when (--any? (equal ms it) possible-ops) - (setq forward t) - (setq s mb) - (when back - (forward-char (length ms)))) - (when (--any? (equal ms it) possible-cls) - (setq forward nil) - (setq e me) - (when (not back) - (backward-char (length ms)))) - (let* ((opens (if forward possible-ops possible-cls)) - (closes (if forward possible-cls possible-ops)) - (needle (sp--strict-regexp-opt (append possible-ops possible-cls))) - (search-fn (if forward 'sp--search-forward-regexp 'sp--search-backward-regexp)) - (depth 1) - (eof (if forward 'eobp 'bobp)) - (b (if forward fw-bound bw-bound)) - (open (substring-no-properties ms)) - (close (substring-no-properties ms)) - (failure (funcall eof)) - (skip-match-pair-fns (->> possible-ops - (--mapcat (-when-let (smf (sp-get-pair it :skip-match)) - (list (cons it smf) (cons (sp-get-pair it :close) smf))))))) - (while (and (> depth 0) (not (funcall eof))) - (sp--search-and-save-match search-fn needle b r mb me ms) - (if r - (unless (or (and (not in-string-or-comment) - (if forward (save-excursion - (backward-char) - (sp-point-in-string-or-comment)) - (sp-point-in-string-or-comment))) - ;; check the individual pair skipper. We - ;; need to test all the possible-ops, - ;; which makes it a bit ugly :/ - (let ((skip-match-pair-fn - (cdr (--first (equal (car it) ms) skip-match-pair-fns)))) - (sp--skip-match-p ms mb me :global-skip global-skip-fn :pair-skip skip-match-pair-fn))) - (when (--any? (equal ms it) opens) (setq depth (1+ depth))) - (when (--any? (equal ms it) closes) (setq depth (1- depth)))) - (unless (minibufferp) - (sp-message :unmatched-expression)) - (setq depth -1) - (setq failure t))) - (if forward - (setq e me) - (setq s mb)) - (setq close (substring-no-properties ms)) - (if (or failure - (/= depth 0)) - (progn + (sp--with-case-sensitive + (save-excursion + (let* ((search-fn (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) + (global-skip-fn (cdr (--first (memq major-mode (car it)) sp-navigate-skip-match))) + (pair-list (sp--get-allowed-pair-list)) + ;; TODO UGLY HACK!!! When the situation is: + ;; ..)|;; comment + ;; the context the point gets is the comment. But if we + ;; are searching backward, that is incorrect, because in + ;; that case we want the context of the closing pair. + ;; Therefore, if the direction is backward, we need to move + ;; one point backward, then test the comment/string thing, + ;; then compute the correct bounds, and then restore the + ;; point so the search will pick up the ) + + ;; However, we need to distinguish the cases where we are + ;; in comment and trying to get out, and when we are in any + ;; context and we jump into string (in that case, we should + ;; report code context!). For example: + ;; "foo"|;bar + ;; or + ;; "foo"|bar + ;; should both report code context + ;; and "|(foo)" should report string context. + + ;; Beware the case when we have a string inside a comment, like + ;; (foo) ;; bar "baz"| qux + ;; In this case we want to report comment context even when + ;; backing into the "" (which however is commented) + + ;; Yet another case is when we are not in a comment but + ;; directly after one and we search backwards, consider: + ;; /* foo bar */| + ;; in C-like language. In this case, we want to report the + ;; context as comment. + + ;; In some languages, special paren syntax with a prefix + ;; serves to mark strings. This means that regular + ;; delimiters, like () are used to delimit strings. For + ;; example, in ruby the sequence %w(...) signifies a + ;; string. If the point is after such a sequence and we + ;; are searching back, we must use the string context, + ;; because the paren is now a string delimiter. This is + ;; usually implemented with "string fence" syntax, so we + ;; will simply check for that. + + ;; Thanks for being consistent at handling syntax bounds Emacs! + (in-string-or-comment (if back + (let ((in-comment (sp-point-in-comment)) + (in-string (sp-point-in-string))) + (save-excursion + (unless (= (point) (point-min)) + (backward-char) + (cond + ((eq (car (syntax-after (point))) 15) (point)) + (in-comment (when (sp-point-in-comment) (1+ (point)))) + ((and (not in-comment) (sp-point-in-comment)) (1+ (point))) + ((or in-comment in-string) (1+ (point))))))) + (when (sp-point-in-string-or-comment) (point)))) + (string-bounds (and in-string-or-comment + (progn + (goto-char in-string-or-comment) + (sp--get-string-or-comment-bounds)))) + (fw-bound (if in-string-or-comment (cdr string-bounds) (point-max))) + (bw-bound (if in-string-or-comment (car string-bounds) (point-min))) + s e forward mb me ms r done + possible-pairs possible-interfering-pairs possible-ops possible-cls) + (while (and (not done) + (sp--search-and-save-match + search-fn + ;; #556 The regexp we use here might exclude or + ;; include extra pairs in case the next match is in + ;; a different context. There's no way to know + ;; beforehand where we land, so we need to consider + ;; *all* pairs in the search and then re-check with + ;; a regexp based on the context of the found pair + (sp--get-allowed-regexp + ;; use all the pairs! + (sp--get-pair-list)) + (if back bw-bound fw-bound) + r mb me ms)) + ;; search for the first opening pair. Here, only consider tags + ;; that are allowed in the current context. + (unless (or (not (save-excursion + (if back + (progn + (goto-char me) + (sp--looking-back-p (sp--get-allowed-regexp))) + (goto-char mb) + (sp--looking-at-p (sp--get-allowed-regexp))))) + (sp--skip-match-p ms mb me :global-skip global-skip-fn)) + ;; if the point originally wasn't inside of a string or comment + ;; but now is, jump out of the string/comment and only search + ;; the code. This ensures that the comments and strings are + ;; skipped if we search inside code. + (if (and (not in-string-or-comment) + (if back + ;; When searching back, the point lands on the + ;; first character of whatever pair we've found + ;; and it is in the proper context, for example + ;; "|(foo)" + (sp-point-in-string-or-comment) + ;; However, when searching forward, the point + ;; lands after the last char of the pair so to get + ;; its context we must back up one character + (sp-point-in-string-or-comment (1- (point))))) + (-if-let (bounds (sp--get-string-or-comment-bounds)) + (let ((jump-to (if back (car bounds) (cdr bounds)))) + (goto-char jump-to) + ;; Can't move out of comment because eob, #427 + (when (eobp) + (setq done t))) + (setq done t)) + (setq done t)))) + (when r + (setq possible-pairs (--filter (or (equal ms (car it)) + (equal ms (cdr it))) + pair-list)) + (setq possible-ops (-map 'car possible-pairs)) + (setq possible-cls (-map 'cdr possible-pairs)) + (setq pair-list (-difference pair-list possible-pairs)) + (setq possible-interfering-pairs pair-list) + (while possible-interfering-pairs + (setq possible-interfering-pairs + (--filter (or (-contains? possible-ops (car it)) + (-contains? possible-cls (cdr it))) + pair-list)) + (setq pair-list (-difference pair-list possible-interfering-pairs)) + (setq possible-ops (append possible-ops (-map 'car possible-interfering-pairs))) + (setq possible-cls (append possible-cls (-map 'cdr possible-interfering-pairs)))) + (when (--any? (equal ms it) possible-ops) + (setq forward t) + (setq s mb) + (when back + (forward-char (length ms)))) + (when (--any? (equal ms it) possible-cls) + (setq forward nil) + (setq e me) + (when (not back) + (backward-char (length ms)))) + (let* ((opens (if forward possible-ops possible-cls)) + (closes (if forward possible-cls possible-ops)) + (needle (sp--strict-regexp-opt (append possible-ops possible-cls))) + (search-fn (if forward 'sp--search-forward-regexp 'sp--search-backward-regexp)) + (depth 1) + (eof (if forward 'eobp 'bobp)) + (b (if forward fw-bound bw-bound)) + (open (substring-no-properties ms)) + (close (substring-no-properties ms)) + (failure (funcall eof)) + (skip-match-pair-fns (->> possible-ops + (--mapcat (-when-let (smf (sp-get-pair it :skip-match)) + (list (cons it smf) (cons (sp-get-pair it :close) smf))))))) + (while (and (> depth 0) (not (funcall eof))) + (sp--search-and-save-match search-fn needle b r mb me ms) + (if r + (unless (or (and (not in-string-or-comment) + (if forward (save-excursion + (backward-char) + (sp-point-in-string-or-comment)) + (sp-point-in-string-or-comment))) + ;; check the individual pair skipper. We + ;; need to test all the possible-ops, + ;; which makes it a bit ugly :/ + (let ((skip-match-pair-fn + (cdr (--first (equal (car it) ms) skip-match-pair-fns)))) + (sp--skip-match-p ms mb me :global-skip global-skip-fn :pair-skip skip-match-pair-fn))) + (when (--any? (equal ms it) opens) (setq depth (1+ depth))) + (when (--any? (equal ms it) closes) (setq depth (1- depth)))) (unless (minibufferp) (sp-message :unmatched-expression)) - nil) - (let ((end-in-cos (sp-point-in-string-or-comment (1- e)))) ;; fix the "point on comment" issue - (cond - ((or (and (sp-point-in-string-or-comment s) (not end-in-cos)) - (and (not (sp-point-in-string-or-comment s)) end-in-cos)) - (unless (minibufferp) - (sp-message :delimiter-in-string)) - nil) - (t - (let* ((op (if forward open close))) - (list :beg s - :end e - :op op - :cl (if forward close open) - :prefix (sp--get-prefix s op) - :suffix (sp--get-suffix e op)))))))))))) + (setq depth -1) + (setq failure t))) + (if forward + (setq e me) + (setq s mb)) + (setq close (substring-no-properties ms)) + (if (or failure + (/= depth 0)) + (progn + (unless (minibufferp) + (sp-message :unmatched-expression)) + nil) + (let ((end-in-cos (sp-point-in-string-or-comment (1- e)))) ;; fix the "point on comment" issue + (cond + ((or (and (sp-point-in-string-or-comment s) (not end-in-cos)) + (and (not (sp-point-in-string-or-comment s)) end-in-cos)) + (unless (minibufferp) + (sp-message :delimiter-in-string)) + nil) + (t + (let* ((op (if forward open close))) + (list :beg s + :end e + :op op + :cl (if forward close open) + :prefix (sp--get-prefix s op) + :suffix (sp--get-suffix e op))))))))))))) ;; TODO: this does not consider unbalanced quotes in comments!!! (defun sp--find-next-stringlike-delimiter (needle search-fn-f &optional limit skip-fn) @@ -3920,13 +4698,16 @@ and the skip-match predicate." (unless (or (save-match-data (save-excursion (goto-char (match-beginning 0)) - (or (looking-back "\\\\") ;; assumes \ is always the escape... bad? + (or (sp--looking-back-p "\\\\" 2) ;; assumes \ is always the escape... bad? (and (eq major-mode 'emacs-lisp-mode) (not (sp-point-in-string)) - (looking-back "?"))))) + (sp--looking-back-p "?" 1))))) ;; TODO: HACK: global-skip is hack here!!! - (sp--skip-match-p match (match-beginning 0) (match-end 0) :pair-skip skip-fn :global-skip nil)) - (setq hit t)))) + (sp--skip-match-p match (match-beginning 0) (match-end 0) + :pair-skip (or skip-fn + (sp-get-pair match :skip-match)) + :global-skip nil)) + (setq hit (match-data))))) hit)) (defun sp-get-stringlike-expression (&optional back) @@ -3934,38 +4715,39 @@ and the skip-match predicate." String-like expression is expression enclosed with the same opening and closing delimiter, such as *...*, \"...\", `...` etc." - (save-excursion - (let ((needle (sp--get-stringlike-regexp)) - (search-fn-f (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) - (search-fn-b (if back 'sp--search-forward-regexp 'sp--search-backward-regexp)) - (count 0) - m b e skip-match-fn limit ok) - (when (not (equal needle "")) - (when (sp--find-next-stringlike-delimiter needle search-fn-f) - ;; assumes \ is always the escape... bad? - (setq m (match-string-no-properties 0)) - (setq needle (regexp-quote m)) - (setq skip-match-fn (sp-get-pair m :skip-match)) - (cond - ((sp-point-in-string) - (setq limit (sp-get-quoted-string-bounds))) - ((sp-point-in-comment) - (setq limit (sp-get-comment-bounds)))) - (save-excursion - (while (sp--find-next-stringlike-delimiter needle 'search-backward-regexp (car limit) skip-match-fn) - (setq count (1+ count)))) - (when (= (mod count 2) 0) - (sp--find-next-stringlike-delimiter needle search-fn-b nil)) - (save-excursion - (setq ok (sp--find-next-stringlike-delimiter needle 'sp--search-backward-regexp (car limit))) - (setq e (match-beginning 0))) - (setq ok (and ok (sp--find-next-stringlike-delimiter needle 'search-forward-regexp (cdr limit)))) - (setq b (match-end 0)) - (when ok - (let ((mb b) (me e)) - (setq b (min mb me)) - (setq e (max mb me))) - (list :beg b :end e :op m :cl m :prefix (sp--get-prefix b m) :suffix (sp--get-suffix e m)))))))) + (sp--with-case-sensitive + (save-excursion + (let ((needle (sp--get-stringlike-regexp)) + (search-fn-f (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) + (search-fn-b (if back 'sp--search-forward-regexp 'sp--search-backward-regexp)) + (count 0) + m b e skip-match-fn limit ok) + (when (not (equal needle "")) + (when (sp--find-next-stringlike-delimiter needle search-fn-f) + ;; assumes \ is always the escape... bad? + (setq m (match-string-no-properties 0)) + (setq needle (regexp-quote m)) + (setq skip-match-fn (sp-get-pair m :skip-match)) + (cond + ((sp-point-in-string) + (setq limit (sp-get-quoted-string-bounds))) + ((sp-point-in-comment) + (setq limit (sp-get-comment-bounds)))) + (save-excursion + (while (sp--find-next-stringlike-delimiter needle 'search-backward-regexp (car limit) skip-match-fn) + (setq count (1+ count)))) + (when (= (mod count 2) 0) + (sp--find-next-stringlike-delimiter needle search-fn-b nil)) + (save-excursion + (setq ok (sp--find-next-stringlike-delimiter needle 'sp--search-backward-regexp (car limit))) + (setq e (match-beginning 0))) + (setq ok (and ok (sp--find-next-stringlike-delimiter needle 'search-forward-regexp (cdr limit)))) + (setq b (match-end 0)) + (when ok + (let ((mb b) (me e)) + (setq b (min mb me)) + (setq e (max mb me))) + (list :beg b :end e :op m :cl m :prefix (sp--get-prefix b m) :suffix (sp--get-suffix e m))))))))) (defun sp--textmode-stringlike-regexp (delimiters &optional direction) "Get a regexp matching text-mode string-like DELIMITERS. @@ -3992,7 +4774,7 @@ If DIRECTION is nil, create a regexp matching both directions." (defun sp--find-next-textmode-stringlike-delimiter (needle search-fn-f &optional limit) "Find the next string-like delimiter, considering the escapes and the skip-match predicate." - (let (hit match) + (let (hit) (while (and (not hit) (funcall search-fn-f needle limit t)) (save-match-data @@ -4008,6 +4790,8 @@ and the skip-match predicate." (defun sp-get-textmode-stringlike-expression (&optional back) "Find the nearest text-mode string-like expression. +If BACK is non-nil search in the backwards direction. + Text-mode string-like expression is one where the delimiters must be surrounded by whitespace from the outside. For example, @@ -4026,6 +4810,8 @@ to non-stringlike matching and we can use a simple counting (stack) algorithm." (save-excursion (let ((restart-from (point)) + (bounds (or (sp-get-comment-bounds) + (cons (point-min) (point-max)))) hit re) (while (not hit) (goto-char restart-from) @@ -4035,9 +4821,10 @@ counting (stack) algorithm." (let* ((delimiters (-map 'car (sp--get-allowed-stringlike-list))) (needle (sp--textmode-stringlike-regexp delimiters)) (search-fn-f (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) - (search-fn-b (if back 'sp--search-forward-regexp 'sp--search-backward-regexp))) - (-if-let ((delim type) (sp--find-next-textmode-stringlike-delimiter needle search-fn-f)) + (limit-f (if (not back) (cdr bounds) (car bounds)))) + (-if-let ((delim type) (sp--find-next-textmode-stringlike-delimiter needle search-fn-f limit-f)) (let ((search-fn (if (eq type :open) 'sp--search-forward-regexp 'sp--search-backward-regexp)) + (limit (if (eq type :open) (cdr bounds) (car bounds))) (needle (sp--textmode-stringlike-regexp (list delim) (if (eq type :open) :close :open)))) (setq restart-from (point)) ;; this adjustments are made because elisp regexp @@ -4049,7 +4836,7 @@ counting (stack) algorithm." (when (and back (eq type :close)) (forward-char (1+ (length delim)))) (when (and back (eq type :open) (not (bobp))) (forward-char 1))) (let ((other-end (point))) - (when (sp--find-next-textmode-stringlike-delimiter needle search-fn) + (when (sp--find-next-textmode-stringlike-delimiter needle search-fn limit) ;; Beware, we also need to test the beg/end of ;; buffer, because we have that variant in the ;; regexp. In that case the match does not @@ -4081,80 +4868,100 @@ counting (stack) algorithm." (defun sp-use-textmode-stringlike-parser-p () "Test if we should use textmode stringlike parser or not." - (let ((modes (-filter 'symbolp sp-nagivate-use-textmode-stringlike-parser)) - (derived (-map 'cdr (-remove 'symbolp sp-nagivate-use-textmode-stringlike-parser)))) + (let ((modes (-filter 'symbolp sp-navigate-use-textmode-stringlike-parser)) + (derived (-map 'cdr (-remove 'symbolp sp-navigate-use-textmode-stringlike-parser)))) (or (--any? (eq major-mode it) modes) (apply 'derived-mode-p derived)))) -(defun sp-get-stringlike-or-textmode-expression (&optional back) - "Return a stringlike expression using stringlike or textmode parser." +(defun sp-get-stringlike-or-textmode-expression (&optional back delimiter) + "Return a stringlike expression using stringlike or textmode parser. + +DELIMITER is a candidate in case we performed a search before +calling this function and we know it's the closest string +delimiter to try. This is purely a performance hack, do not rely +on it when calling directly." (if (sp-use-textmode-stringlike-parser-p) (sp-get-textmode-stringlike-expression back) ;; performance hack. If the delimiter is a character in ;; syntax class 34, grab the string-like expression using ;; `sp-get-string' - (if (and (= (length (match-string 0)) 1) - (eq (char-syntax (string-to-char (match-string 0))) 34)) - (sp-get-string back) + (if (and delimiter + (= (length delimiter) 1) + ;; TODO: this "smart" behaviour is duplicated in + ;; `sp-get-thing', maybe the whole string parsing could + ;; be extracted to some common function (actually we + ;; should probably use this one from `sp-get-thing') + (eq (char-syntax (string-to-char delimiter)) 34)) + (if (eq t (sp-point-in-string)) + ;; TODO: this is duplicated in `sp-get-thing', move to a function + (save-excursion + (save-restriction + (widen) + (-let (((beg . end) (sp-get-quoted-string-bounds))) + (narrow-to-region beg end)) + (sp-get-stringlike-expression back))) + (sp-get-string back)) (sp-get-stringlike-expression back)))) (defun sp-get-expression (&optional back) "Find the nearest balanced expression of any kind. -See also: `sp-navigate-consider-stringlike-sexp'. - For markup and text modes a special, more efficient stringlike parser is available, see `sp-get-textmode-stringlike-expression'. By default, this is enabled in all modes derived from `text-mode'. You can change it by customizing -`sp-nagivate-use-textmode-stringlike-parser'." - (if (memq major-mode sp-navigate-consider-stringlike-sexp) - (let ((pre (sp--get-allowed-regexp)) - (sre (sp--get-stringlike-regexp)) - (search-fn (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) - (ps (if back (1- (point-min)) (1+ (point-max)))) - (ss (if back (1- (point-min)) (1+ (point-max))))) - (setq ps (or (save-excursion (funcall search-fn pre nil t)) ps)) - (setq ss (or (save-excursion (funcall search-fn sre nil t)) ss)) - ;; TODO: simplify this logic somehow... (this really depends - ;; on a rewrite of the core parser logic: separation of "find - ;; the valid opening" and "parse it") - - ;; Here, we sacrifice readability for performance. Because we - ;; only use regexp to look forward for the closest pair, it - ;; might occasionally happen that what we picked in fact - ;; *can't* form a pair and it returns error (for example, it - ;; is an unclosed pair or a quote between words like'so, which - ;; doesn't form a pair). In such a case, or when the pair - ;; found is further than the other possible pair type (for - ;; example, we think we should parse stringlike, but we skip - ;; the first occurrence and the next one is only after a - ;; regular pair, which we should've picked instead), we must - ;; try the other parser as well. - (-let (((type . re) (if (or (and (not back) (< ps ss)) - (and back (> ps ss))) - (cons :regular (sp-get-paired-expression back)) - (cons :string (sp-get-stringlike-or-textmode-expression back))))) - (if re - (sp-get re - (cond - ;; If the returned sexp is regular, but the - ;; to-be-tried-string-expression is before it, we try - ;; to parse it as well, it might be a complete sexp in - ;; which case it should be returned. - ((and (eq type :regular) - (or (and (not back) (< ss :beg)) - (and back (> ss :end)))) - (or (sp-get-stringlike-or-textmode-expression back) re)) - ((and (eq type :string) - (or (and (not back) (< ps :beg)) - (and back (> ps :end)))) - (or (sp-get-paired-expression back) re)) - (t re))) - (if (eq type :regular) - (sp-get-stringlike-or-textmode-expression back) - (sp-get-paired-expression back))))) - (sp-get-paired-expression back))) +`sp-navigate-use-textmode-stringlike-parser'." + (let ((pre (sp--get-allowed-regexp)) + (sre (sp--get-stringlike-regexp)) + (search-fn (if (not back) 'sp--search-forward-regexp 'sp--search-backward-regexp)) + (ps (if back (1- (point-min)) (1+ (point-max)))) + (ss (if back (1- (point-min)) (1+ (point-max)))) + (string-delim nil)) + (setq ps (if (equal pre "") ps + (or (save-excursion (funcall search-fn pre nil t)) ps))) + (setq ss (if (equal sre "") ss + (or (--when-let (save-excursion + (sp--find-next-stringlike-delimiter sre search-fn)) + (setq string-delim (match-string 0)) + (save-match-data + (set-match-data it) + (if back (match-beginning 0) (match-end 0)))) + ss))) + ;; TODO: simplify this logic somehow... (this really depends + ;; on a rewrite of the core parser logic: separation of "find + ;; the valid opening" and "parse it") + + ;; Here, we sacrifice readability for performance. Because we + ;; only use regexp to look forward for the closest pair, it + ;; might occasionally happen that what we picked in fact + ;; *can't* form a pair and it returns error (for example, it + ;; is an unclosed pair or a quote between words like'so, which + ;; doesn't form a pair). In such a case, or when the pair + ;; found is further than the other possible pair type (for + ;; example, we think we should parse stringlike, but we skip + ;; the first occurrence and the next one is only after a + ;; regular pair, which we should've picked instead), we must + ;; try the other parser as well. + (-let (((type . re) (if (or (and (not back) (< ps ss)) + (and back (> ps ss))) + (cons :regular (sp-get-paired-expression back)) + (cons :string (sp-get-stringlike-or-textmode-expression back string-delim))))) + (when re + (sp-get re + (cond + ;; If the returned sexp is regular, but the + ;; to-be-tried-string-expression is before it, we try + ;; to parse it as well, it might be a complete sexp in + ;; which case it should be returned. + ((and (eq type :regular) + (or (and (not back) (< ss :beg)) + (and back (> ss :end)))) + (or (sp-get-stringlike-or-textmode-expression back string-delim) re)) + ((and (eq type :string) + (or (and (not back) (< ps :beg)) + (and back (> ps :end)))) + (or (sp-get-paired-expression back) re)) + (t re))))))) (defun sp-get-sexp (&optional back) "Find the nearest balanced expression that is after (before) point. @@ -4163,10 +4970,7 @@ Search backward if BACK is non-nil. This also means, if the point is inside an expression, this expression is returned. If `major-mode' is member of `sp-navigate-consider-sgml-tags', -sgml tags will also be considered as sexps in current buffer. If -`major-mode' is member of `sp-navigate-consider-stringlike-sexp', -expressions where the opening and closing delimiters are the same -will also be considered as sexps. +sgml tags will also be considered as sexps in current buffer. If the search starts outside a comment, all subsequent comments are skipped. @@ -4188,44 +4992,46 @@ The return value is a plist with following keys: :op - opening delimiter :cl - closing delimiter :prefix - expression prefix + :suffix - expression suffix However, you should never access this structure directly as it is subject to change. Instead, use the macro `sp-get' which also provide shortcuts for many commonly used queries (such as length of opening/closing delimiter or prefix)." (sp--maybe-init) - (cond - (sp-prefix-tag-object - (sp-get-sgml-tag back)) - (sp-prefix-pair-object - (sp-get-paired-expression back)) - ((memq major-mode sp-navigate-consider-sgml-tags) - (let ((paired (sp-get-expression back))) - (if (and paired - (equal "<" (sp-get paired :op))) - ;; if the point is inside the tag delimiter, return the pair. - (if (sp-get paired (and (<= :beg-in (point)) (>= :end-in (point)))) - paired - ;; if the tag can't be completed, we can at least return - ;; the <> pair - (or (sp-get-sgml-tag back) paired)) - ;; we can still try the tag if the first < or > is closer than - ;; the pair. This is a bit too complicated... seems like a - ;; more clever solution would be needed in the future, esp if - ;; we add the python hack. - (cond - ((and (not back) - (< (save-excursion - (or (search-forward "<" nil t) (point-max))) - (or (sp-get paired :beg) (point-max)))) - (or (sp-get-sgml-tag) paired)) - ((and back - (> (save-excursion - (or (search-backward ">" nil t) (point-min))) - (or (sp-get paired :end) (point-max)))) - (or (sp-get-sgml-tag t) paired)) - (t paired))))) - (t (sp-get-expression back)))) + (sp--with-case-sensitive + (cond + (sp-prefix-tag-object + (sp-get-sgml-tag back)) + (sp-prefix-pair-object + (sp-get-paired-expression back)) + ((memq major-mode sp-navigate-consider-sgml-tags) + (let ((paired (sp-get-expression back))) + (if (and paired + (equal "<" (sp-get paired :op))) + ;; if the point is inside the tag delimiter, return the pair. + (if (sp-get paired (and (<= :beg-in (point)) (>= :end-in (point)))) + paired + ;; if the tag can't be completed, we can at least return + ;; the <> pair + (or (sp-get-sgml-tag back) paired)) + ;; we can still try the tag if the first < or > is closer than + ;; the pair. This is a bit too complicated... seems like a + ;; more clever solution would be needed in the future, esp if + ;; we add the python hack. + (cond + ((and (not back) + (< (save-excursion + (or (search-forward "<" nil t) (point-max))) + (or (sp-get paired :beg) (point-max)))) + (or (sp-get-sgml-tag) paired)) + ((and back + (> (save-excursion + (or (search-backward ">" nil t) (point-min))) + (or (sp-get paired :end) (point-max)))) + (or (sp-get-sgml-tag t) paired)) + (t paired))))) + (t (sp-get-expression back))))) (defun sp--get-hybrid-sexp-beg () "Get the beginning of hybrid sexp. @@ -4363,13 +5169,12 @@ argument." (while (and ok (>= (sp-get ok :beg) p)) (setq ok (sp-get-sexp)) (when ok (goto-char (sp-get ok :end))))))) - ;; if the pair expression is completely enclosed inside a - ;; string, return the pair expression, otherwise return the - ;; string expression + ;; if the pair expression is enclosed inside a string, return + ;; the pair expression, otherwise return the string expression (when okr (unless (and ok - (sp-compare-sexps ok okr >) - (sp-compare-sexps ok okr < :end)) + (sp-compare-sexps ok okr >=) + (sp-compare-sexps ok okr <= :end)) (setq ok okr) (goto-char (sp-get ok :end)))) (setq n (1- n))) @@ -4423,23 +5228,37 @@ and the prefix is searched backwards. If the prefix property is defined for OP, the associated regexp is used to retrieve the prefix instead of the global setting." - (let ((pref (sp-get-pair op :prefix))) + (sp--with-case-sensitive (save-excursion (goto-char p) - (if pref - (when (sp--looking-back pref) - (match-string-no-properties 0)) - (-if-let (mmode-prefix (cdr (assoc major-mode sp-sexp-prefix))) - (cond - ((and (eq (car mmode-prefix) 'regexp) - (sp--looking-back (cadr mmode-prefix))) - (match-string-no-properties 0)) - ((eq (car mmode-prefix) 'syntax) - (skip-syntax-backward (cadr mmode-prefix)) - (buffer-substring-no-properties (point) p)) - (t "")) - (skip-syntax-backward "'") - (buffer-substring-no-properties (point) p)))))) + (let* ((pref (sp-get-pair op :prefix)) + (prefix + (if pref + (when (sp--looking-back pref sp-max-prefix-length) + (match-string-no-properties 0)) + (-if-let (mmode-prefix (cdr (assoc major-mode sp-sexp-prefix))) + (cond + ((and (eq (car mmode-prefix) 'regexp) + (sp--looking-back (cadr mmode-prefix))) + (match-string-no-properties 0)) + ((eq (car mmode-prefix) 'syntax) + (skip-syntax-backward (cadr mmode-prefix)) + (buffer-substring-no-properties (point) p)) + (t "")) + (backward-prefix-chars) + (buffer-substring-no-properties (point) p))))) + ;; do not consider it a prefix if it matches some opening or + ;; closing delimiter which is allowed for parsing in current + ;; context + (goto-char p) + (if (and (< 0 (length prefix)) + (or (sp--do-action-p prefix 'navigate) + (sp--do-action-p + (car (--first (equal (cdr it) prefix) + sp-pair-list)) + 'navigate))) + "" + prefix))))) (cl-defun sp--get-suffix (&optional (p (point)) op) "Get the suffix of EXPR. @@ -4451,23 +5270,37 @@ See `sp-sexp-suffix'. If the suffix property is defined for OP, the associated regexp is used to retrieve the suffix instead of the global setting." - (let ((suff (sp-get-pair op :suffix))) + (sp--with-case-sensitive (save-excursion (goto-char p) - (if suff - (when (sp--looking-at suff) - (match-string-no-properties 0)) - (-if-let (mmode-suffix (cdr (assoc major-mode sp-sexp-suffix))) - (cond - ((and (eq (car mmode-suffix) 'regexp) - (sp--looking-at (cadr mmode-suffix))) - (match-string-no-properties 0)) - ((eq (car mmode-suffix) 'syntax) - (skip-syntax-forward (cadr mmode-suffix)) - (buffer-substring-no-properties p (point))) - (t "")) - (skip-syntax-forward ".") - (buffer-substring-no-properties p (point))))))) + (let* ((suff (sp-get-pair op :suffix)) + (suffix + (if suff + (when (sp--looking-at suff) + (match-string-no-properties 0)) + (-if-let (mmode-suffix (cdr (assoc major-mode sp-sexp-suffix))) + (cond + ((and (eq (car mmode-suffix) 'regexp) + (sp--looking-at (cadr mmode-suffix))) + (match-string-no-properties 0)) + ((eq (car mmode-suffix) 'syntax) + (skip-syntax-forward (cadr mmode-suffix)) + (buffer-substring-no-properties p (point))) + (t "")) + (skip-syntax-forward ".") + (buffer-substring-no-properties p (point)))))) + ;; do not consider it a suffix if it matches some opening or + ;; closing delimiter which is allowed for parsing in current + ;; context + (goto-char p) + (if (and (< 0 (length suffix)) + (or (sp--do-action-p suffix 'navigate) + (sp--do-action-p + (car (--first (equal (cdr it) suffix) + sp-pair-list)) + 'navigate))) + "" + suffix))))) (defun sp-get-symbol (&optional back) "Find the nearest symbol that is after point, or before point if BACK is non-nil. @@ -4479,7 +5312,7 @@ returned. Symbol is defined as a chunk of text recognized by The return value is a plist with the same format as the value returned by `sp-get-sexp'." (sp--maybe-init) - (let (b e prefix last-or-first) + (let (b e last-or-first) (save-excursion (if back (progn @@ -4498,26 +5331,22 @@ returned by `sp-get-sexp'." (unless last-or-first (list :beg b :end e :op "" :cl "" :prefix (sp--get-prefix b) :suffix (sp--get-suffix e))))) -;; this +/- 1 nonsense comes from sp-get-quoted-string-bounds. That -;; should go to hell after the parser rewrite (defun sp--get-string (bounds) "Return the `sp-get-sexp' format info about the string. This function simply transforms BOUNDS, which is a cons (BEG . END) into format compatible with `sp-get-sexp'." - (let* ((bob (= (point-min) (car bounds))) - (eob (= (point-max) (cdr bounds))) - ;; if the closing and opening isn't the same token, we should - ;; return nil - (op (char-to-string (char-after (car bounds)))) + (let* ((op (char-to-string (char-after (car bounds)))) (cl (char-to-string (char-before (cdr bounds))))) + ;; if the closing and opening isn't the same token, we should + ;; return nil (when (equal op cl) (list :beg (car bounds) :end (cdr bounds) :op cl :cl cl - :prefix "" - :suffix "")))) + :prefix (sp--get-prefix (car bounds) op) + :suffix (sp--get-suffix (cdr bounds) cl))))) (defun sp-get-string (&optional back) "Find the nearest string after point, or before if BACK is non-nil. @@ -4568,60 +5397,60 @@ and newline." (defun sp-get-sgml-tag (&optional back) (sp--maybe-init) - (save-excursion - (let ((search-fn (if (not back) 'sp--search-forward-regexp 'search-backward-regexp)) - (case-fold-search nil) - tag tag-name needle - open-start open-end - close-start close-end) - (when (and (funcall search-fn "" nil t) - (progn - (setq tag (substring-no-properties (match-string 0))) - (setq tag-name (sp--sgml-get-tag-name tag)) - (not (sp--sgml-ignore-tag tag-name)))) - (setq needle (concat "" nil t) - (setq open-end (point)))) - (cond - ((and (not back) (not forward)) - (goto-char (match-beginning 0))) - ((and back forward) - (goto-char (match-end 0)))) - (while (> depth 0) - (if (funcall search-fn needle nil t) - (if (sp--sgml-opening-p (match-string 0)) - (if forward (setq depth (1+ depth)) (setq depth (1- depth))) - (if forward (setq depth (1- depth)) (setq depth (1+ depth)))) - (setq depth -1))) - (if (eq depth -1) - (progn (sp-message :no-matching-tag) nil) + (sp--with-case-sensitive + (save-excursion + (let ((search-fn (if (not back) 'sp--search-forward-regexp 'search-backward-regexp)) + tag tag-name needle + open-start open-end + close-start close-end) + (when (and (funcall search-fn "" nil t) + (progn + (setq tag (substring-no-properties (match-string 0))) + (setq tag-name (sp--sgml-get-tag-name tag)) + (not (sp--sgml-ignore-tag tag-name)))) + (setq needle (concat "" nil t) - (setq close-end (point))) - (setq close-start (point)) + (setq open-end (point)) + (search-backward-regexp "<" nil t) + (setq open-start (point))) + (setq open-start (point)) (search-forward-regexp ">" nil t) - (setq close-end (point)))) - (let ((op (buffer-substring-no-properties open-start open-end)) - (cl (buffer-substring-no-properties close-start close-end))) - (list :beg (if forward open-start close-start) - :end (if forward close-end open-end) - :op (if forward op cl) - :cl (if forward cl op) - :prefix "" - :suffix "")))))))) + (setq open-end (point)))) + (cond + ((and (not back) (not forward)) + (goto-char (match-beginning 0))) + ((and back forward) + (goto-char (match-end 0)))) + (while (> depth 0) + (if (funcall search-fn needle nil t) + (if (sp--sgml-opening-p (match-string 0)) + (if forward (setq depth (1+ depth)) (setq depth (1- depth))) + (if forward (setq depth (1- depth)) (setq depth (1+ depth)))) + (setq depth -1))) + (if (eq depth -1) + (progn (sp-message :no-matching-tag) nil) + (save-excursion + (if forward + (progn + (setq close-start (match-beginning 0)) + (search-forward-regexp ">" nil t) + (setq close-end (point))) + (setq close-start (point)) + (search-forward-regexp ">" nil t) + (setq close-end (point)))) + (let ((op (buffer-substring-no-properties open-start open-end)) + (cl (buffer-substring-no-properties close-start close-end))) + (list :beg (if forward open-start close-start) + :end (if forward close-end open-end) + :op (if forward op cl) + :cl (if forward cl op) + :prefix "" + :suffix ""))))))))) (defun sp--end-delimiter-closure (pairs pair-list) "Compute the \"end-delimiter\" closure of set PAIRS. @@ -4702,11 +5531,9 @@ You can also bind the output of this function directly to a key, like: This will be a function that descends down only into { } pair, ignoring all others." - (lexical-let ((pairs pairs) - (function function)) - (lambda (&optional arg) - (interactive "P") - (sp-restrict-to-pairs pairs function)))) + (lambda (&optional _arg) + (interactive "P") + (sp-restrict-to-pairs pairs function))) (defun sp-restrict-to-object-interactive (object function) "Return an interactive lambda that calls FUNCTION restricted to OBJECT. @@ -4728,13 +5555,11 @@ You can also bind the output of this function directly to a key, like: This will be a function that navigates only by using paired expressions, ignoring strings and sgml tags." - (lexical-let ((object object) - (function function)) - (lambda (&optional arg) - (interactive "P") - (sp-restrict-to-object object function)))) + (lambda (&optional _arg) + (interactive "P") + (sp-restrict-to-object object function))) -(defun sp-prefix-tag-object (&optional arg) +(defun sp-prefix-tag-object (&optional _arg) "Read the command and invoke it on the next tag object. If you specify a regular emacs prefix argument this is passed to @@ -4752,7 +5577,7 @@ Tag object is anything delimited by sgml tag." (call-interactively com) (execute-kbd-macro cmd)))) -(defun sp-prefix-pair-object (&optional arg) +(defun sp-prefix-pair-object (&optional _arg) "Read the command and invoke it on the next pair object. If you specify a regular emacs prefix argument this is passed to @@ -4769,7 +5594,7 @@ Pair object is anything delimited by pairs from `sp-pair-list'." (call-interactively com) (execute-kbd-macro cmd)))) -(defun sp-prefix-symbol-object (&optional arg) +(defun sp-prefix-symbol-object (&optional _arg) "Read the command and invoke it on the next pair object. If you specify a regular emacs prefix argument this is passed to @@ -4787,7 +5612,7 @@ Symbol is defined as a chunk of text recognized by (call-interactively com) (execute-kbd-macro cmd)))) -(defun sp-prefix-save-excursion (&optional arg) +(defun sp-prefix-save-excursion (&optional _arg) "Execute the command keeping the point fixed. If you specify a regular emacs prefix argument this is passed to @@ -4795,10 +5620,11 @@ the executed command." (interactive "P") (let* ((cmd (read-key-sequence "" t)) (com (key-binding cmd))) - (save-excursion - (if (commandp com) - (call-interactively com) - (execute-kbd-macro cmd))))) + (sp--keep-indentation + (save-excursion + (if (commandp com) + (call-interactively com) + (execute-kbd-macro cmd)))))) (defun sp-get-thing (&optional back) "Find next thing after point, or before if BACK is non-nil. @@ -4810,86 +5636,135 @@ string (`sp-get-string') or balanced expression recognized by If `sp-navigate-consider-symbols' is nil, only balanced expressions are considered." (sp--maybe-init) - (cond - (sp-prefix-tag-object (sp-get-sgml-tag back)) - (sp-prefix-pair-object (sp-get-paired-expression back)) - (sp-prefix-symbol-object (sp-get-symbol back)) - (t - (if back + (sp--with-case-sensitive + (cond + (sp-prefix-tag-object (sp-get-sgml-tag back)) + (sp-prefix-pair-object (sp-get-paired-expression back)) + (sp-prefix-symbol-object (sp-get-symbol back)) + (t + (if back + (if (not sp-navigate-consider-symbols) + (sp-get-sexp t) + (save-excursion + (cond + ((sp-point-in-empty-string) + (sp-get-string t)) + (t + (sp-skip-backward-to-symbol t nil t) + (cond + ;; this is an optimization, we do not need to look up + ;; the "pair" expression first. If this fails, follow + ;; up with regular sexps + ((and (memq major-mode sp-navigate-consider-sgml-tags) + (or (sp--looking-back ">") + ;; sp-skip-backward-to-symbol moves the + ;; point to the end of an element name in + ;; js2-jsx-mode + (looking-at ">")) + (sp-get-sgml-tag t))) + ((sp--valid-initial-delimiter-p (sp--looking-back (sp--get-closing-regexp (sp--get-allowed-pair-list)) nil)) + (sp-get-sexp t)) + ((sp--valid-initial-delimiter-p (sp--looking-back (sp--get-opening-regexp (sp--get-allowed-pair-list)) nil)) + (sp-get-sexp t)) + ((and (eq (syntax-class (syntax-after (1- (point)))) 7) + (not (sp-char-is-escaped-p (1- (point))))) + (if (eq t (sp-point-in-string)) + (save-excursion + (save-restriction + (widen) + (-let (((beg . end) (sp-get-quoted-string-bounds))) + (narrow-to-region beg end)) + (sp-get-stringlike-expression t))) + (sp-get-string t))) + ((sp--valid-initial-delimiter-p (sp--looking-back (sp--get-stringlike-regexp) nil)) + (sp-get-expression t)) + ;; We might be somewhere inside the prefix of the + ;; sexp after the point. Since the prefix can be + ;; specified as regexp and not syntax class, it might + ;; itself by a symbol which would invalidly get + ;; picked here. + (t (-when-let (sym (sp-get-symbol t)) + (save-excursion + (sp-get sym (goto-char :end)) + (if (sp--valid-initial-delimiter-p (sp--looking-at (sp--get-opening-regexp (sp--get-allowed-pair-list)))) + (let* ((ms (match-string 0)) + (pref (sp--get-prefix (point) ms))) + (if (and pref + (not (equal pref ""))) + (sp-get-sexp t) + sym)) + sym))))))))) (if (not sp-navigate-consider-symbols) - (sp-get-sexp t) + (sp-get-sexp nil) (save-excursion (cond ((sp-point-in-empty-string) - (sp-get-string t)) + (sp-get-string nil)) (t - (sp-skip-backward-to-symbol t nil t) + (sp-skip-forward-to-symbol t nil t) (cond - ;; this is an optimization, we do not need to look up - ;; the "pair" expression first. If this fails, follow - ;; up with regular sexps ((and (memq major-mode sp-navigate-consider-sgml-tags) - (sp--looking-back ">") - (sp-get-sgml-tag t))) - ((sp--valid-initial-delimiter-p (sp--looking-back (sp--get-closing-regexp (sp--get-allowed-pair-list)) nil)) - (sp-get-sexp t)) - ((sp--valid-initial-delimiter-p (sp--looking-back (sp--get-opening-regexp (sp--get-allowed-pair-list)) nil)) - (sp-get-sexp t)) - ((eq (char-syntax (preceding-char)) 34) - (sp-get-string t)) - ((and (memq major-mode sp-navigate-consider-stringlike-sexp) - (sp--valid-initial-delimiter-p (sp--looking-back (sp--get-stringlike-regexp) nil)) - (sp-get-expression t))) - (t (sp-get-symbol t))))))) - (if (not sp-navigate-consider-symbols) - (sp-get-sexp nil) - (save-excursion - (cond - ((sp-point-in-empty-string) - (sp-get-string nil)) - (t - (sp-skip-forward-to-symbol t nil t) - (cond - ((and (memq major-mode sp-navigate-consider-sgml-tags) - (looking-at "<") - (sp-get-sgml-tag))) - ((sp--valid-initial-delimiter-p (sp--looking-at (sp--get-opening-regexp (sp--get-allowed-pair-list)))) - (sp-get-sexp nil)) - ((sp--valid-initial-delimiter-p (sp--looking-at (sp--get-closing-regexp (sp--get-allowed-pair-list)))) - (sp-get-sexp nil)) - ((eq (char-syntax (following-char)) 34) - (sp-get-string nil)) - ((and (memq major-mode sp-navigate-consider-stringlike-sexp) - (sp--valid-initial-delimiter-p (sp--looking-at (sp--get-stringlike-regexp))) - (sp-get-expression nil))) - ;; it can still be that we are looking at a /prefix/ of a - ;; sexp. We should skip a symbol forward and check if it - ;; is a sexp, and then maybe readjust the output. - (t (let* ((sym (sp-get-symbol nil)) - (sym-string (and sym (sp-get sym (buffer-substring-no-properties :beg :end)))) - (point-before-prefix (point))) - (when sym-string - (if (sp--valid-initial-delimiter-p (sp--search-forward-regexp (sp--get-opening-regexp (sp--get-allowed-pair-list)) nil t)) - (let* ((ms (match-string 0)) - (pref (progn - ;; need to move before the - ;; opening, so (point) evals - ;; there. - (backward-char (length ms)) - (sp--get-prefix (point) ms)))) - ;; We use >= because the first skip to - ;; symbol might have skipped some prefix - ;; chars which make prefix of the symbol - ;; which together make prefix of a sexp. - ;; For example \foo{} in latex, where \ is - ;; prefix of symbol foo and \foo is prefix - ;; of { - (if (and pref - (not (equal pref "")) - (>= point-before-prefix (- (point) (length pref)))) - (sp-get-sexp nil) - sym)) - sym))))))))))))) + (or (looking-at "<") + ;; sp-skip-forward-to-symbol moves the point + ;; to the beginning of an element name in + ;; js2-jsx-mode + (and (sp--looking-back "= because the first skip to + ;; symbol might have skipped some prefix + ;; chars which make prefix of the symbol + ;; which together make prefix of a sexp. + ;; For example \foo{} in latex, where \ is + ;; prefix of symbol foo and \foo is prefix + ;; of { + (if (and pref + (not (equal pref "")) + (>= point-before-prefix (- (point) (length pref)))) + (sp-get-sexp nil) + sym)) + sym)))))))))))))) (defun sp-narrow-to-sexp (arg) "Make text outside current balanced expression invisible. @@ -4976,6 +5851,11 @@ at current level, jump one level up (effectively doing `sp-backward-up-sexp'). Negative arg -N means move to the beginning of N-th previous balanced expression. +If `sp-navigate-interactive-always-progress-point' is non-nil, +and this is called interactively, the point will move to the +first expression in forward direction where it will end up +greater than the current location. + With `sp-navigate-consider-symbols' symbols and strings are also considered balanced expressions. @@ -4983,10 +5863,26 @@ Examples: ((foo) |bar (baz quux)) -> ((foo) bar |(baz quux)) - ((foo) bar |(baz quux)) -> |((foo) bar (baz quux))" + ((foo) bar |(baz quux)) -> |((foo) bar (baz quux)) + +and with non-nil `sp-navigate-interactive-always-progress-point' + + (f|oo bar) -> (foo |bar) + + ((fo|o) (bar)) -> ((foo) |(bar))" (interactive "^p") (setq arg (or arg 1)) - (if (> arg 0) + (if (<= arg 0) + (sp-backward-sexp (- arg)) + (if (and sp-navigate-interactive-always-progress-point + (called-interactively-p 'any)) + (progn + (while (< 0 arg) + (let ((point-start (point))) + (while (--when-let (sp-forward-sexp) + (<= (sp-get it :beg) point-start)))) + (setq arg (1- arg))) + (goto-char (sp-get (sp-get-thing t) :beg))) (if (= arg 1) (-when-let (ok (sp-get-thing)) (if (= (point) (sp-get ok :beg)) @@ -4995,8 +5891,7 @@ Examples: (goto-char (sp-get ok :beg)) ok)) (sp-forward-sexp arg) - (sp-backward-sexp)) - (sp-backward-sexp (- arg)))) + (sp-backward-sexp))))) (put 'sp-next-sexp 'CUA 'move) @@ -5011,14 +5906,36 @@ N-th following balanced expression. With `sp-navigate-consider-symbols' symbols and strings are also considered balanced expressions. +If `sp-navigate-interactive-always-progress-point' is non-nil, +and this is called interactively, the point will move to the +first expression in backward direction where it will end up +less than the current location. + Examples: ((foo) bar| (baz quux)) -> ((foo)| bar (baz quux)) - ((foo)| bar (baz quux)) -> ((foo) bar (baz quux))|" + ((foo)| bar (baz quux)) -> ((foo) bar (baz quux))| + +and if `sp-navigate-interactive-always-progress-point' is non-nil + + (foo b|ar baz) -> (foo| bar baz) + + (foo (b|ar baz)) -> (foo| (bar baz))" (interactive "^p") (setq arg (or arg 1)) - (if (> arg 0) + (if (<= arg 0) + (sp-forward-sexp (- arg)) + (if (and sp-navigate-interactive-always-progress-point + ;; (called-interactively-p 'any) + ) + (progn + (while (< 0 arg) + (let ((point-start (point))) + (while (--when-let (sp-backward-sexp) + (>= (sp-get it :end) point-start)))) + (setq arg (1- arg))) + (goto-char (sp-get (sp-get-thing) :end))) (if (= arg 1) (-when-let (ok (sp-get-thing t)) (if (= (point) (sp-get ok :end)) @@ -5027,11 +5944,76 @@ Examples: (goto-char (sp-get ok :end)) ok)) (sp-backward-sexp arg) - (sp-forward-sexp)) - (sp-forward-sexp (- arg)))) + (sp-forward-sexp))))) (put 'sp-previous-sexp 'CUA 'move) +(defun sp-forward-parallel-sexp (&optional arg) + "Move forward across one balanced expressions at the same depth. + +If calling `sp-forward-sexp' at point would result in raising a +level up, loop back to the first expression at current level, +that is the first child of the enclosing sexp as defined by +`sp-get-enclosing-sexp'." + (interactive "^p") + (setq arg (or arg 1)) + (if (< arg 0) + (sp-backward-parallel-sexp (- arg)) + (let (re) + (while (> arg 0) + (setq arg (1- arg)) + (let ((next (sp-get-thing)) + (prev (sp-get-thing t))) + (setq + re + (cond + ((eq next nil) + (goto-char (point-min)) + (sp-forward-sexp)) + ((eq prev nil) + (goto-char (sp-get next :end)) + next) + (t (if (> (sp-get next :beg) (sp-get prev :beg)) + (progn + (goto-char (sp-get next :end)) + next) + (goto-char (sp-get next :beg-in)) + (sp-forward-sexp))))))) + re))) + +(defun sp-backward-parallel-sexp (&optional arg) + "Move backward across one balanced expressions at the same depth. + +If calling `sp-backward-sexp' at point would result in raising a +level up, loop back to the last expression at current level, that +is the last child of the enclosing sexp as defined by +`sp-get-enclosing-sexp'." + (interactive "^p") + (setq arg (or arg 1)) + (if (< arg 0) + (sp-forward-parallel-sexp (- arg)) + (let (re) + (while (> arg 0) + (setq arg (1- arg)) + (let ((next (sp-get-thing)) + (prev (sp-get-thing t))) + (setq + re + (cond + ((eq prev nil) + (goto-char (point-max)) + (sp-backward-sexp)) + ((eq next nil) + (goto-char (sp-get prev :beg)) + prev) + (t (if (< (sp-get prev :end) (sp-get next :end)) + (progn + (goto-char (sp-get prev :beg)) + prev) + (goto-char (sp-get prev :end-in)) + (sp-backward-sexp))))))) + re))) + (defun sp--raw-argument-p (arg) "Return t if ARG represents raw argument, that is a non-empty list." (and (listp arg) (car arg))) @@ -5246,6 +6228,9 @@ Examples: (defun sp-beginning-of-next-sexp (&optional arg) "Jump to the beginning of next sexp on the same depth. +Optional argument ARG defaults to 1 and means how many times we +should repeat. + This acts exactly as `sp-beginning-of-sexp' but adds 1 to the numeric argument. @@ -5267,6 +6252,9 @@ Examples: (defun sp-beginning-of-previous-sexp (&optional arg) "Jump to the beginning of previous sexp on the same depth. +Optional argument ARG defaults to 1 and means how many times we +should repeat. + This acts exactly as `sp-beginning-of-sexp' with negative argument but subtracts 1 from it. @@ -5288,6 +6276,9 @@ Examples: (defun sp-end-of-next-sexp (&optional arg) "Jump to the end of next sexp on the same depth. +Optional argument ARG defaults to 1 and means how many times we +should repeat. + This acts exactly as `sp-end-of-sexp' but adds 1 to the numeric argument. @@ -5309,6 +6300,9 @@ Examples: (defun sp-end-of-previous-sexp (&optional arg) "Jump to the end of previous sexp on the same depth. +Optional argument ARG defaults to 1 and means how many times we +should repeat. + This acts exactly as `sp-end-of-sexp' with negative argument but subtracts 1 from it. @@ -5327,6 +6321,11 @@ Examples: (put 'sp-end-of-previous-sexp 'CUA 'move) +;; TODO: split the reindent code so we can call it inside strings on +;; sexps like [foo ]... We can't reindent that by default because it +;; can be a regular expression or something where the whitespace +;; matters. For now, disable reindent in strings if the sexp is not +;; the string quote itself. (defun sp-up-sexp (&optional arg interactive) "Move forward out of one level of parentheses. @@ -5354,65 +6353,72 @@ Examples: (foo |(bar baz) -> (foo)| (bar baz) ;; close unbalanced expr." (interactive "^p\np") (setq arg (or arg 1)) - (let ((ok (sp-get-enclosing-sexp (abs arg)))) - (if ok - (progn - (if (> arg 0) - (goto-char (sp-get ok :end)) - (goto-char (sp-get ok :beg))) - (when (and (= (abs arg) 1) - (not (equal (sp-get ok :prefix) sp-comment-char)) - (or (memq major-mode (assq 'always sp-navigate-reindent-after-up)) - (and (memq major-mode (assq 'interactive sp-navigate-reindent-after-up)) - interactive)) - (if sp-navigate-reindent-after-up-in-string - t - (save-excursion - (sp-get ok - (goto-char :end-in) - (not (sp-point-in-string)))))) - ;; TODO: this needs different indent rules for different - ;; modes. Should we concern with such things? Lisp rules are - ;; funny in HTML... :/ + (sp--with-case-sensitive + (let ((ok (sp-get-enclosing-sexp (abs arg)))) + (if ok + (progn + (if (> arg 0) + (goto-char (sp-get ok :end)) + (goto-char (sp-get ok :beg))) + (when (and (= (abs arg) 1) + (not (equal (sp-get ok :prefix) sp-comment-char)) + (or (memq major-mode (assq 'always sp-navigate-reindent-after-up)) + (and (memq major-mode (assq 'interactive sp-navigate-reindent-after-up)) + interactive)) + (or sp-navigate-reindent-after-up-in-string + (sp-get ok (not (sp-point-in-string :end-in)))) + ;; if the sexp to be reindented is not a string + ;; but is inside a string, we should rather do + ;; nothing than break semantics (in e.g. regexp + ;; [...]) + (let ((str (sp-point-in-string))) + (or (not str) + ;; op must be the delimiter of the string we're in + (eq (sp-get ok :op) + (or (eq str t) + (char-to-string str)))))) + ;; TODO: this needs different indent rules for different + ;; modes. Should we concern with such things? Lisp rules are + ;; funny in HTML... :/ + (save-excursion + (if (> arg 0) + (progn + (goto-char (sp-get ok :end-in)) + (let ((prev (sp-get-thing t))) + ;; if the expression is empty remove everything inside + (if (sp-compare-sexps ok prev) + (sp-get ok (delete-region :beg-in :end-in)) + (when (save-excursion + (skip-chars-backward " \t\n") + (= (point) (sp-get prev :end-suf))) + (delete-region (sp-get prev :end-suf) (point)))))) + (goto-char (sp-get ok :beg-in)) + (let ((next (sp-get-thing))) + (if (sp-compare-sexps ok next) + (sp-get ok (delete-region :beg-in :end-in)) + (when (save-excursion + (skip-chars-forward " \t\n") + (= (point) (sp-get next :beg-prf))) + (delete-region (point) (sp-get next :beg-prf))))))))) + ;; on forward up, we can detect that the pair was not closed. + ;; Therefore, jump sexps backwards until we hit the error, then + ;; extract the opening pair and insert it at point. Only works + ;; for pairs defined in `sp-pair-list'. + (when (and (> arg 0) + sp-navigate-close-if-unbalanced) + (let (active-pair) (save-excursion - (if (> arg 0) - (progn - (goto-char (sp-get ok :end-in)) - (let ((prev (sp-get-thing t))) - ;; if the expression is empty remove everything inside - (if (sp-compare-sexps ok prev) - (sp-get ok (delete-region :beg-in :end-in)) - (when (save-excursion - (skip-chars-backward " \t\n") - (= (point) (sp-get prev :end-suf))) - (delete-region (sp-get prev :end-suf) (point)))))) - (goto-char (sp-get ok :beg-in)) - (let ((next (sp-get-thing))) - (if (sp-compare-sexps ok next) - (sp-get ok (delete-region :beg-in :end-in)) - (when (save-excursion - (skip-chars-forward " \t\n") - (= (point) (sp-get next :beg-prf))) - (delete-region (point) (sp-get next :beg-prf))))))))) - ;; on forward up, we can detect that the pair was not closed. - ;; Therefore, jump sexps backwards until we hit the error, then - ;; extract the opening pair and insert it at point. Only works - ;; for pairs defined in `sp-pair-list'. - (when (and (> arg 0) - sp-navigate-close-if-unbalanced) - (let (active-pair) - (save-excursion - ;; add support for SGML tags here - (while (sp-backward-sexp)) - (sp-skip-backward-to-symbol t) - (when (sp--looking-back (sp--get-opening-regexp)) - (let* ((op (match-string 0))) - (setq active-pair (assoc op sp-pair-list))))) - (when active-pair - (sp-backward-sexp) - (sp-forward-sexp) - (insert (cdr active-pair)))))) - ok)) + ;; add support for SGML tags here + (while (sp-backward-sexp)) + (sp-skip-backward-to-symbol t) + (when (sp--looking-back (sp--get-opening-regexp)) + (let* ((op (match-string 0))) + (setq active-pair (assoc op sp-pair-list))))) + (when active-pair + (sp-backward-sexp) + (sp-forward-sexp) + (insert (cdr active-pair)))))) + ok))) (put 'sp-up-sexp 'CUA 'move) @@ -5449,6 +6455,24 @@ Examples: If the next command is `sp-kill-sexp', append the whitespace between the successive kills.") +(defun sp--kill-or-copy-region (beg end &optional dont-kill) + "Kill or copy region between BEG and END according to DONT-KILL. +If `evil-mode' is active, copying a region will also add it to the 0 register. +Additionally, if command was prefixed with a register, copy the region +to that register." + (interactive) + (let ((result + (if dont-kill + (copy-region-as-kill beg end) + (kill-region beg end)))) + (when (bound-and-true-p evil-mode) + (when dont-kill + (evil-set-register ?0 (evil-get-register ?1))) + (when evil-this-register + (evil-set-register evil-this-register (evil-get-register ?1)) + (setq evil-this-register nil))) + result)) + (defun sp-kill-sexp (&optional arg dont-kill) "Kill the balanced expression following point. @@ -5511,8 +6535,7 @@ Note: prefix argument is shown after the example in (n (abs arg)) (ok t) (b (point-max)) - (e (point-min)) - (kill-fn (if dont-kill 'copy-region-as-kill 'kill-region))) + (e (point))) (cond ;; kill to the end or beginning of list ((and raw @@ -5524,8 +6547,10 @@ Note: prefix argument is shown after the example in (let ((del (sp-get-whitespace))) (sp-get del (delete-region :beg :end)))) (if (> arg 0) - (funcall kill-fn (sp-get next :beg-prf) (sp-get enc :end-in)) - (funcall kill-fn (sp-get next :end) (sp-get enc :beg-in))) + (sp--kill-or-copy-region + (sp-get next :beg-prf) (sp-get enc :end-in) dont-kill) + (sp--kill-or-copy-region + (sp-get next :end) (sp-get enc :beg-in) dont-kill)) (when (not dont-kill) (let ((del (sp-get-whitespace))) (sp-get del (delete-region :beg :end))))))) @@ -5533,12 +6558,14 @@ Note: prefix argument is shown after the example in ((and raw (= n 16)) (let ((lst (sp-backward-up-sexp))) - (sp-get lst (funcall kill-fn :beg-prf :end)))) + (sp-get lst (sp--kill-or-copy-region + :beg-prf :end dont-kill)))) ;; kill inside of sexp ((= n 0) (let ((e (sp-get-enclosing-sexp))) (when e - (sp-get e (funcall kill-fn :beg-in :end-in))))) + (sp-get e (sp--kill-or-copy-region + :beg-in :end-in dont-kill))))) ;; regular kill (t (save-excursion @@ -5554,8 +6581,9 @@ Note: prefix argument is shown after the example in (progn (when (member sp-successive-kill-preserve-whitespace '(1 2)) (kill-append sp-last-kill-whitespace nil)) - (funcall kill-fn (if (> b (point)) (point) b) e)) - (funcall kill-fn b e)) + (sp--kill-or-copy-region + (if (> b (point)) (point) b) e dont-kill)) + (sp--kill-or-copy-region b e dont-kill)) ;; kill useless junk whitespace, but only if we're actually ;; killing the region (when (not dont-kill) @@ -5570,11 +6598,11 @@ Note: prefix argument is shown after the example in (kill-append sp-last-kill-whitespace nil))))))))) (defun sp--cleanup-after-kill () - (unless (looking-back "^[\t\s]+") + (unless (save-match-data (looking-back "^[\t\s]+" (1- (line-beginning-position)))) (let ((bdel (save-excursion - (when (looking-back " ") + (when (sp--looking-back-p " " 1) (skip-chars-backward " \t") - (when (not (looking-back (sp--get-opening-regexp))) + (when (not (sp--looking-back-p (sp--get-opening-regexp))) (forward-char))) (point))) (edel (save-excursion @@ -5591,8 +6619,13 @@ Note: prefix argument is shown after the example in ""))) (delete-region bdel edel))) (if (memq major-mode sp-lisp-modes) + ;; WARNING: The above white-space killing routine might preserve + ;; less whitespace than there actually is because the indent + ;; might further eat some up (indent-according-to-mode) - (unless (memq major-mode sp-no-reindent-after-kill-modes) + (unless (or (memq major-mode sp-no-reindent-after-kill-modes) + (memq indent-line-function + sp-no-reindent-after-kill-indent-line-functions)) (save-excursion (sp--indent-region (line-beginning-position) (line-end-position))) (when (> (save-excursion @@ -5705,7 +6738,8 @@ Examples: (arg (prefix-numeric-value arg)) (orig-indent (save-excursion (back-to-indentation) - (current-column)))) + (current-column))) + (orig-column (current-column))) (cond ((= arg 0) (kill-line)) ((and raw (= arg 16)) @@ -5720,31 +6754,63 @@ Examples: (sp-point-in-symbol)) (sp-backward-sexp)) (sp-get hl - (kill-region (point) (min (point-max) (if (looking-at "[ \t]*$") (1+ :end-suf) :end-suf))) - (when sp-hybrid-kill-excessive-whitespace - (cond - ((sp-point-in-blank-line) - (while (and (not (eobp)) - (sp-point-in-blank-line)) - (delete-region (line-beginning-position) (min (point-max) (1+ (line-end-position)))))) - ((looking-at "[ \t]*$") - (delete-blank-lines))))))) + (let ((end (min (point-max) (if (looking-at "[ \t]*$") + (1+ :end-suf) + :end-suf)))) + (when sp-hybrid-kill-excessive-whitespace + (save-excursion + (goto-char end) + (skip-chars-forward "\n\t\r\s") + (cond + ((eq 'kill sp-hybrid-kill-excessive-whitespace) + (setq end (point))) + (t (delete-region end (point)))))) + (kill-region (point) end))))) (sp--cleanup-after-kill) ;; if we've killed the entire line, do *not* contract the indent ;; to just one space (when (sp-point-in-blank-line) (delete-region (line-beginning-position) (line-end-position)) - (let ((need-indent (- orig-indent (current-column)))) - (when (> need-indent 0) - (insert (make-string need-indent ?\ ))))))))) + (if (and (= 0 orig-column) + kill-whole-line) + (delete-char 1) ;; delete the newline + (let ((need-indent (- orig-indent (current-column)))) + (when (> need-indent 0) + (insert (make-string need-indent ?\ )))))))))) + +(defun sp-kill-whole-line () + "Kill current line in sexp-aware manner. + +First, go to the beginning of current line and then try to kill +as much as possible on the current line but without breaking +balance. + +If there is a hanging sexp at the end of line the it is killed as +well. + +If there is a closing delimiter for a sexp \"up\" current sexp, +the kill is not extended after it. For more details see +`sp-kill-hybrid-sexp'. + +Examples: + + (progn (progn + (some |long sexp)) -> |)" + (interactive) + (beginning-of-line) + (sp-kill-hybrid-sexp nil) + (let ((empty-last-line (save-excursion (beginning-of-line) (eobp)))) + ;; We can't kill the line if it is empty and the last line + (when (and (sp-point-in-blank-line) (not empty-last-line)) + (kill-whole-line)))) (defun sp--transpose-objects (first second) "Transpose FIRST and SECOND object while preserving the whitespace between them." (save-excursion (goto-char (sp-get second :beg-prf)) - (let ((ins (sp-get second (delete-and-extract-region :beg-prf :end-suf))) - (between (delete-and-extract-region (sp-get first :end-suf) (point)))) + (let ((ins (sp-get second (delete-and-extract-region :beg-prf :end))) + (between (delete-and-extract-region (sp-get first :end) (point)))) (goto-char (sp-get first :beg-prf)) (insert ins between)))) @@ -5775,8 +6841,7 @@ Examples: foo bar baz| -> foo baz| bar ;; -1" (interactive "P") - (let* ((raw (sp--raw-argument-p arg)) - (arg (prefix-numeric-value arg)) + (let* ((arg (prefix-numeric-value arg)) (n (abs arg))) ;; if we're inside a symbol, we need to move out of it first (when (> arg 0) @@ -6033,7 +7098,7 @@ Examples: (if (> (prefix-numeric-value arg) 0) (let ((n (abs (prefix-numeric-value arg))) (enc (sp-get-enclosing-sexp)) - (ins-space 0) + (in-comment (sp-point-in-comment)) next-thing ok) (when enc (save-excursion @@ -6043,45 +7108,73 @@ Examples: (setq next-thing (sp-get-enclosing-sexp)) (when next-thing (goto-char (sp-get next-thing :end-in)) - (sp--run-hook-with-args (sp-get enc :op) :pre-handlers 'slurp-forward) + (sp--run-hook-with-args (sp-get enc :op) :pre-handlers 'slurp-forward + (list :arg arg :enc enc :next-thing next-thing)) (sp-get enc (insert :cl :suffix)) (goto-char (sp-get enc :end-suf)) (delete-char (sp-get enc (- (+ :cl-l :suffix-l)))) (sp--indent-region (sp-get enc :beg-prf) (sp-get next-thing :end)) - (sp--run-hook-with-args (sp-get enc :op) :post-handlers 'slurp-forward))) + (sp--run-hook-with-args (sp-get enc :op) :post-handlers 'slurp-forward + (list :arg arg :enc enc :next-thing next-thing)))) (while (> n 0) (goto-char (sp-get enc :end-suf)) (setq ok enc) (setq next-thing (sp-get-thing nil)) - (setq ins-space 0) (while (sp-compare-sexps next-thing ok <) (goto-char (sp-get next-thing :end-suf)) (setq ok next-thing) (setq next-thing (sp-get-thing nil))) - (if ok + ;; do not allow slurping into a different context from + ;; inside a comment + (if (and in-comment + (save-excursion + (sp-get next-thing + (goto-char :beg) + (not (sp-point-in-comment))))) (progn - (if (and (equal (sp-get next-thing :cl) "\"") - (equal (sp-get ok :cl) "\"")) - (progn - (sp--join-sexp ok next-thing) - (goto-char (- (sp-get next-thing :end) 2)) - (plist-put enc :end (- (sp-get next-thing :end) 2))) - (delete-char (sp-get ok (- (+ :cl-l :suffix-l)))) - (when (and (sp-get ok (/= :len-in 0)) - (= (sp-get ok :end-suf) (sp-get next-thing :beg-prf))) - (insert " ") - (setq ins-space -1)) - ;; this calculation corrects the absence of already deleted cls - (goto-char (- (sp-get next-thing :end-suf) (sp-get ok (+ :cl-l :suffix-l)) ins-space)) - (sp--run-hook-with-args (sp-get enc :op) :pre-handlers 'slurp-forward) - (sp-get ok (insert :cl :suffix)) - (sp--indent-region (sp-get ok :beg-prf) (point)) - ;; HACK: update the "enc" data structure if ok==enc - (when (= (sp-get enc :beg) (sp-get ok :beg)) (plist-put enc :end (point))) - (sp--run-hook-with-args (sp-get enc :op) :post-handlers 'slurp-forward)) - (setq n (1- n))) - (sp-message :cant-slurp) - (setq n -1))))))) + (sp-message :cant-slurp-context) + (setq n -1)) + (if ok + (progn + (if (and (equal (sp-get next-thing :cl) "\"") + (equal (sp-get ok :cl) "\"")) + (progn + (sp--join-sexp ok next-thing) + (goto-char (- (sp-get next-thing :end) 2)) + (plist-put enc :end (- (sp-get next-thing :end) 2))) + (let ((inner-sexp + (save-excursion + (goto-char (sp-get ok :end-in)) + (sp-get-thing t)))) + (delete-char (sp-get ok (- (+ :cl-l :suffix-l)))) + ;; this calculation corrects the absence + ;; of already deleted cls + (goto-char (- (sp-get next-thing :end-suf) + (sp-get ok (+ :cl-l :suffix-l)))) + ;; only insert space if not inserting it + ;; would merge two sexps together + (when (and (sp-get ok (/= :len-in 0)) + (sp-compare-sexps + inner-sexp + (sp-get-thing t)) + (= (sp-get ok :end-suf) + (sp-get next-thing :beg-prf))) + (save-excursion + (goto-char (sp-get ok :end-in)) + (insert " ")))) + (sp--run-hook-with-args + (sp-get enc :op) :pre-handlers 'slurp-forward + (list :arg arg :enc enc :ok ok :next-thing next-thing)) + (sp-get ok (insert :cl :suffix)) + (sp--indent-region (sp-get ok :beg-prf) (point)) + ;; HACK: update the "enc" data structure if ok==enc + (when (= (sp-get enc :beg) (sp-get ok :beg)) (plist-put enc :end (point))) + (sp--run-hook-with-args + (sp-get enc :op) :post-handlers 'slurp-forward + (list :arg arg :enc enc :ok ok :next-thing next-thing))) + (setq n (1- n))) + (sp-message :cant-slurp) + (setq n -1)))))))) (sp-backward-slurp-sexp (sp--negate-argument arg)))) (defun sp-backward-slurp-sexp (&optional arg) @@ -6116,6 +7209,7 @@ Examples: (if (> (prefix-numeric-value arg) 0) (let ((n (abs (prefix-numeric-value arg))) (enc (sp-get-enclosing-sexp)) + (in-comment (sp-point-in-comment)) next-thing ok) (when enc (save-excursion @@ -6126,10 +7220,14 @@ Examples: (when next-thing (delete-char (sp-get enc (+ :op-l :prefix-l))) (goto-char (sp-get next-thing :beg-in)) - (sp--run-hook-with-args (sp-get enc :op) :pre-handlers 'slurp-backward) + (sp--run-hook-with-args + (sp-get enc :op) :pre-handlers 'slurp-backward + (list :arg arg :enc enc :next-thing next-thing)) (sp-get enc (insert :prefix :op)) (sp--indent-region (sp-get next-thing :beg-in) (sp-get enc :end)) - (sp--run-hook-with-args (sp-get enc :op) :post-handlers 'slurp-backward))) + (sp--run-hook-with-args + (sp-get enc :op) :post-handlers 'slurp-backward + (list :arg arg :enc enc :next-thing next-thing)))) (while (> n 0) (goto-char (sp-get enc :beg-prf)) (setq ok enc) @@ -6138,28 +7236,54 @@ Examples: (goto-char (sp-get next-thing :beg-prf)) (setq ok next-thing) (setq next-thing (sp-get-thing t))) - (if ok + ;; do not allow slurping into a different context from + ;; inside a comment + (if (and in-comment + (save-excursion + (sp-get next-thing + (goto-char :beg) + (not (sp-point-in-comment))))) (progn - (if (and (equal (sp-get next-thing :cl) "\"") - (equal (sp-get ok :cl) "\"")) - (progn - (sp--join-sexp next-thing ok) + (sp-message :cant-slurp-context) + (setq n -1)) + (if ok + (progn + (if (and (equal (sp-get next-thing :cl) "\"") + (equal (sp-get ok :cl) "\"")) + (progn + (sp--join-sexp next-thing ok) + (goto-char (sp-get next-thing :beg-prf)) + (plist-put enc :beg (sp-get next-thing :beg))) + (let ((inner-sexp + (save-excursion + (goto-char (sp-get ok :beg-in)) + (sp-get-thing)))) + (delete-char (sp-get ok (+ :op-l :prefix-l))) (goto-char (sp-get next-thing :beg-prf)) - (plist-put enc :beg (sp-get next-thing :beg))) - (delete-char (sp-get ok (+ :op-l :prefix-l))) - (when (and (sp-get ok (/= :len-in 0)) - (= (sp-get ok :beg-prf) (sp-get next-thing :end-suf))) - (insert " ")) - (goto-char (sp-get next-thing :beg-prf)) - (sp--run-hook-with-args (sp-get enc :op) :pre-handlers 'slurp-backward) - (sp-get ok (insert :prefix :op)) - (sp--indent-region (point) (sp-get ok :end)) - ;; HACK: update the "enc" data structure if ok==enc - (when (sp-compare-sexps enc ok) (plist-put enc :beg (- (point) (sp-get ok :op-l)))) - (sp--run-hook-with-args (sp-get enc :op) :post-handlers 'slurp-backward)) - (setq n (1- n))) - (sp-message :cant-slurp) - (setq n -1))))))) + ;; only insert space if not inserting it + ;; would merge two sexps together + (when (and (sp-get ok (/= :len-in 0)) + (= (sp-get ok (- (sp-get inner-sexp :end) + :op-l :prefix-l)) + (sp-get (sp-get-thing) :end)) + (= (sp-get ok :beg-prf) + (sp-get next-thing :end-suf))) + (save-excursion + (goto-char (sp-get ok (- :beg-in :op-l :prefix-l))) + (insert " ")))) + (sp--run-hook-with-args + (sp-get enc :op) :pre-handlers 'slurp-backward + (list :arg arg :enc enc :ok ok :next-thing next-thing)) + (sp-get ok (insert :prefix :op)) + (sp--indent-region (point) (sp-get ok :end)) + ;; HACK: update the "enc" data structure if ok==enc + (when (sp-compare-sexps enc ok) (plist-put enc :beg (- (point) (sp-get ok :op-l)))) + (sp--run-hook-with-args + (sp-get enc :op) :post-handlers 'slurp-backward + (list :arg arg :enc enc :ok ok :next-thing next-thing))) + (setq n (1- n))) + (sp-message :cant-slurp) + (setq n -1)))))))) (sp-forward-slurp-sexp (sp--negate-argument arg)))) (defun sp-add-to-previous-sexp (&optional arg) @@ -6273,11 +7397,13 @@ Examples: (prefix arg in comment) (if (sp-compare-sexps back enc) (goto-char :beg-in) (goto-char (sp-get back :end-suf)))) - (sp--run-hook-with-args :op :pre-handlers 'barf-forward)) + (sp--run-hook-with-args :op :pre-handlers 'barf-forward + (list :arg arg :enc enc))) (sp-get (sp-get-enclosing-sexp) (sp-do-move-cl (point)) (sp--indent-region :beg :end) - (sp--run-hook-with-args :op :post-handlers 'barf-forward))))) + (sp--run-hook-with-args :op :post-handlers 'barf-forward + (list :arg arg :enc enc)))))) (sp-backward-barf-sexp (sp--negate-argument old-arg))))) (defun sp-backward-barf-sexp (&optional arg) @@ -6317,11 +7443,13 @@ Examples: (if (sp-compare-sexps next enc) (goto-char :end-in) (goto-char (sp-get next :beg-prf)))) - (sp--run-hook-with-args :op :pre-handlers 'barf-backward)) + (sp--run-hook-with-args :op :pre-handlers 'barf-backward + (list :arg arg :enc enc))) (sp-get (sp-get-enclosing-sexp) (sp-do-move-op (point)) (sp--indent-region :beg :end) - (sp--run-hook-with-args :op :post-handlers 'barf-backward))))) + (sp--run-hook-with-args :op :post-handlers 'barf-backward + (list :arg arg :enc enc)))))) (sp-forward-barf-sexp (sp--negate-argument old-arg))))) ;; TODO: get rid of the macro anyway, it's stupid! @@ -6332,6 +7460,7 @@ Examples: (forward-fn (if forward 'forward-char 'backward-char)) (next-char-fn (if forward 'following-char 'preceding-char)) (looking (if forward 'sp--looking-at 'sp--looking-back)) + (prefix-fn (if forward 'sp--get-suffix 'sp--get-prefix)) (eob-test (if forward '(eobp) '(bobp))) (comment-bound (if forward 'cdr 'car))) `(let ((in-comment (sp-point-in-comment)) @@ -6339,7 +7468,10 @@ Examples: ;; pair that was not allowed before. However, such a call is ;; never made in SP, so it's OK for now (allowed-pairs (sp--get-allowed-regexp)) - (allowed-strings (sp--get-stringlike-regexp))) + (allowed-open (sp--get-opening-regexp (sp--get-allowed-pair-list))) + (allowed-close (sp--get-closing-regexp (sp--get-allowed-pair-list))) + (allowed-strings (sp--get-stringlike-regexp)) + (prefix nil)) (while (and (not (or ,eob-test (and stop-after-string (not (sp-point-in-string)) @@ -6350,15 +7482,38 @@ Examples: (and stop-inside-string (sp-point-in-string) (not (sp-point-in-string (,inc (point))))) - (,looking allowed-pairs) - (and (memq major-mode sp-navigate-consider-stringlike-sexp) - (,looking allowed-strings)))) + (and (,looking allowed-pairs) + (or in-comment (not (sp-point-in-comment)))) + (and (,looking allowed-strings) + (or in-comment (not (sp-point-in-comment)))))) (or (member (char-syntax (,next-char-fn)) '(?< ?> ?! ?| ?\ ?\\ ?\" ?' ?.)) - (unless in-comment (sp-point-in-comment)))) - (when (and (not in-comment) - (sp-point-in-comment)) - (goto-char (,comment-bound (sp-get-comment-bounds)))) - (when (not ,eob-test) (,forward-fn 1)))))) + (/= 0 (logand (lsh 1 20) (car (syntax-after + ,(if forward + '(point) + '(1- (point))))))) + (unless in-comment (sp-point-in-comment)) + ;; This is the case where we are starting at + ;; pair (looking at it) and there is some + ;; prefix which is not recognized by syntax, + ;; i.e. defined by regexp. This should only be + ;; tested once in principle before the next + ;; time we land on a delimiter this whole loop + ;; stops based on the first branch of the `and' + ;; condition in `while' so using expensive + ;; functions here is not a bg deal. + (and (or (,(if forward 'sp--looking-back 'sp--looking-at) + ,(if forward 'allowed-close 'allowed-open)) + (,(if forward 'sp--looking-back 'sp--looking-at) allowed-strings)) + (progn + (setq prefix (,prefix-fn)) + (> (length prefix) 0))))) + (if (and (not in-comment) + (sp-point-in-comment)) + (progn + (goto-char (,comment-bound (sp-get-comment-bounds))) + (unless ,eob-test (,forward-fn 1))) + (unless ,eob-test + (,forward-fn (max (length prefix) 1)))))))) (defun sp-skip-forward-to-symbol (&optional stop-at-string stop-after-string stop-inside-string) "Skip whitespace and comments moving forward. @@ -6435,35 +7590,36 @@ Examples: |foo (bar (baz) quux) -> foo (bar (baz) quux|) ;; 4" (interactive "^p") (setq arg (or arg 1)) - (let* ((n (abs arg)) - (fw (> arg 0)) - (allowed (sp--get-allowed-pair-list)) - (open (sp--get-opening-regexp allowed)) - (close (sp--get-closing-regexp allowed))) - (if fw - (while (> n 0) - ;; First we need to get to the beginning of a symbol. This means - ;; skipping all whitespace and pair delimiters until we hit - ;; something in \sw or \s_ - (while (cond - ((eobp) nil) - ((not (memq (char-syntax (following-char)) '(?w ?_))) - (forward-char) - t) - ;; if allowed is empty, the regexp matches anything - ;; and we go into infinite loop, cf. Issue #400 - ((and allowed (sp--valid-initial-delimiter-p (sp--looking-at open))) - (goto-char (match-end 0))) - ((and allowed (sp--valid-initial-delimiter-p (sp--looking-at close))) - (goto-char (match-end 0))))) - (while (and (not (eobp)) - (or (not allowed) - (not (or (sp--valid-initial-delimiter-p (sp--looking-at open)) - (sp--valid-initial-delimiter-p (sp--looking-at close))))) - (memq (char-syntax (following-char)) '(?w ?_))) - (forward-char)) - (setq n (1- n))) - (sp-backward-symbol n)))) + (sp--with-case-sensitive + (let* ((n (abs arg)) + (fw (> arg 0)) + (allowed (sp--get-allowed-pair-list)) + (open (sp--get-opening-regexp allowed)) + (close (sp--get-closing-regexp allowed))) + (if fw + (while (> n 0) + ;; First we need to get to the beginning of a symbol. This means + ;; skipping all whitespace and pair delimiters until we hit + ;; something in \sw or \s_ + (while (cond + ((eobp) nil) + ((not (memq (char-syntax (following-char)) '(?w ?_))) + (forward-char) + t) + ;; if allowed is empty, the regexp matches anything + ;; and we go into infinite loop, cf. Issue #400 + ((and allowed (sp--valid-initial-delimiter-p (sp--looking-at open))) + (goto-char (match-end 0))) + ((and allowed (sp--valid-initial-delimiter-p (sp--looking-at close))) + (goto-char (match-end 0))))) + (while (and (not (eobp)) + (or (not allowed) + (not (or (sp--valid-initial-delimiter-p (sp--looking-at open)) + (sp--valid-initial-delimiter-p (sp--looking-at close))))) + (memq (char-syntax (following-char)) '(?w ?_))) + (forward-char)) + (setq n (1- n))) + (sp-backward-symbol n))))) (put 'sp-forward-symbol 'CUA 'move) @@ -6490,28 +7646,33 @@ Examples: (quux ((foo) bar) baz)| -> (|quux ((foo) bar) baz) ;; 4" (interactive "^p") (setq arg (or arg 1)) - (let ((n (abs arg)) - (fw (> arg 0)) - (open (sp--get-opening-regexp (sp--get-allowed-pair-list))) - (close (sp--get-closing-regexp (sp--get-allowed-pair-list)))) - (if fw - (while (> n 0) - (while (cond - ((bobp) nil) - ((not (memq (char-syntax (preceding-char)) '(?w ?_))) - (backward-char) - t) - ((sp--valid-initial-delimiter-p (sp--looking-back open)) - (goto-char (match-beginning 0))) - ((sp--valid-initial-delimiter-p (sp--looking-back close)) - (goto-char (match-beginning 0))))) - (while (and (not (bobp)) - (not (or (sp--valid-initial-delimiter-p (sp--looking-back open)) - (sp--valid-initial-delimiter-p (sp--looking-back close)))) - (memq (char-syntax (preceding-char)) '(?w ?_))) - (backward-char)) - (setq n (1- n))) - (sp-forward-symbol n)))) + (sp--with-case-sensitive + (let ((n (abs arg)) + (fw (> arg 0)) + (open (sp--get-opening-regexp (sp--get-allowed-pair-list))) + (close (sp--get-closing-regexp (sp--get-allowed-pair-list)))) + (if fw + (while (> n 0) + (while (cond + ((bobp) nil) + ((not (memq (char-syntax (preceding-char)) '(?w ?_))) + (backward-char) + t) + ((sp--valid-initial-delimiter-p (sp--looking-back open)) + (goto-char (match-beginning 0))) + ((sp--valid-initial-delimiter-p (sp--looking-back close)) + (goto-char (match-beginning 0))))) + (while (and (not (bobp)) + (not (or (sp--valid-initial-delimiter-p (sp--looking-back open)) + (sp--valid-initial-delimiter-p (sp--looking-back close)))) + (memq (char-syntax (preceding-char)) '(?w ?_))) + (backward-char)) + ;; skip characters which are symbols with prefix flag + (while (and (not (eobp)) + (/= 0 (logand (lsh 1 20) (car (syntax-after (point)))))) + (forward-char 1)) + (setq n (1- n))) + (sp-forward-symbol n))))) (put 'sp-backward-symbol 'CUA 'move) @@ -6536,27 +7697,43 @@ Examples: (foo |bar baz) -> [(foo |bar baz)] ;; \\[universal-argument] [" (interactive (list - (let ((available-pairs (sp--get-pair-list-context)) - ev ac (pair-prefix "")) - (while (not ac) - (setq ev (read-event (format "Rewrap with: %s" pair-prefix) t)) - (setq pair-prefix (concat pair-prefix (format-kbd-macro (vector ev)))) - (unless (--any? (string-prefix-p pair-prefix (car it)) available-pairs) - (user-error "Impossible pair prefix selected: %s" pair-prefix)) - (setq ac (--first (equal pair-prefix (car it)) available-pairs))) - ac) + (catch 'done + (let ((available-pairs (sp--get-pair-list-context 'wrap)) + ev ac (pair-prefix "")) + (while (not ac) + (setq ev (read-event (format "Rewrap with: %s" pair-prefix) t)) + (if (and (equal pair-prefix "") + (eq ev 'return)) + (throw 'done nil)) + (setq pair-prefix (concat pair-prefix (format-kbd-macro (vector ev)))) + (unless (--any? (string-prefix-p pair-prefix (car it)) available-pairs) + (user-error "Impossible pair prefix selected: %s" pair-prefix)) + (setq ac (--first (equal pair-prefix (car it)) available-pairs))) + ac)) current-prefix-arg)) - (-when-let (enc (sp-get-enclosing-sexp)) - (save-excursion - (sp-get enc - (goto-char :end) - (unless keep-old - (delete-char (- :cl-l))) - (insert (cdr pair)) - (goto-char :beg) - (insert (car pair)) - (unless keep-old - (delete-char :op-l)))))) + (if (not pair) + (sp-unwrap-sexp) + (-when-let (enc (sp-get-enclosing-sexp)) + (save-excursion + (sp-get enc + (goto-char :end) + (unless keep-old + (delete-char (- :cl-l))) + (insert (cdr pair)) + (goto-char :beg) + (insert (car pair)) + (unless keep-old + (delete-char :op-l)) + (setq sp-last-wrapped-region + (sp--get-last-wraped-region + :beg (+ :end + (length (car pair)) + (length (cdr pair)) + (- :op-l) + (- :cl-l)) + (car pair) (cdr pair))))) + (sp--run-hook-with-args (car pair) :post-handlers 'rewrap-sexp + (list :parent (sp-get enc :op)))))) (defun sp-swap-enclosing-sexp (&optional arg) "Swap the enclosing delimiters of this and the parent expression. @@ -6618,10 +7795,61 @@ represent a valid object in a buffer!" (let ((b (bounds-of-thing-at-point 'line))) (delete-region (car b) (cdr b)))) (setq indent-from (point))) - (unless (memq major-mode sp-no-reindent-after-kill-modes) + (unless (or (memq major-mode sp-no-reindent-after-kill-modes) + (memq indent-line-function + sp-no-reindent-after-kill-indent-line-functions)) (sp--keep-indentation (sp--indent-region indent-from indent-to)))))) +(defun sp-change-inner () + "Change the inside of the next expression. + +First, kill the inside of the next balanced expression, then move +point just after the opening delimiter. + +Examples: + + (f|oo [bar] baz) -> (foo [|] baz) + + {|'foo': 'bar'} -> {'|': 'bar'}" + (interactive) + (-when-let (ok (sp-get-sexp)) + (sp-get ok + (kill-region :beg-in :end-in) + (goto-char :beg-in)))) + +(defun sp-change-enclosing () + "Change the inside of the enclosing expression. + +Whitespace on both sides of the inner items is preserved if it +contains newlines. Invoking this function on a blank sexp will +wipe out remaining whitespace (see `sp-point-in-blank-sexp'). + +Move the point to the beginning of the original content. + +Examples: + + (f|oo [bar] baz) -> (|) + + {'f|oo': 'bar'} -> {'|': 'bar'}" + (interactive) + (-when-let (ok (sp-get-enclosing-sexp)) + (sp-get ok + (if (sp-point-in-blank-sexp) + (progn + (kill-region :beg-in :end-in) + (goto-char :beg-in)) + (let ((beg (progn + (goto-char :beg-in) + (skip-chars-forward "\t\n ") + (point))) + (end (progn + (goto-char :end-in) + (skip-chars-backward "\t\n ") + (point)))) + (kill-region beg end) + (goto-char beg)))))) + (defun sp-unwrap-sexp (&optional arg) "Unwrap the following expression. @@ -6820,8 +8048,7 @@ Examples: ;; abstract (skip-chars-forward "\t\n ") (sp-get-enclosing-sexp 1))) - (sp-get-enclosing-sexp 1)))) - str) + (sp-get-enclosing-sexp 1))))) (when ok (when (and (sp-point-in-comment) (not inside-comment-inside-sexp)) @@ -6854,7 +8081,7 @@ Examples: (sp-point-in-comment) (save-excursion (skip-chars-backward "\t ") - (looking-back "^"))) + (bolp))) (cons (point) (sp-get next :end-suf))))) ;; similarly, if there is a comment before ;; this sexp, keep it. @@ -6866,7 +8093,7 @@ Examples: (> (point) (sp-get ok :beg)) (save-excursion (skip-chars-backward "\t ") - (looking-back "^"))) + (bolp))) (cons (point) (sp-get next :end-suf))))) (t (sp-get next (cons :beg-prf :end-suf)))))) (sp--splice-sexp-do-killing from to @@ -6882,6 +8109,8 @@ splice the resulting expression. Wrap the current enclosing list with the delimiters of the spliced list and insert the saved expressions. +If point is in a symbol, move to end of symbol before convolving. + With ARG positive N, move up N lists before wrapping. Examples: @@ -6898,6 +8127,10 @@ We want to move the `while' before the `let'. (forward-char (sp-get env |:op-l)) -> (sp-get env (forward-char |:op-l))" (interactive "p") (save-excursion + (when (sp-point-in-symbol) + (sp-forward-symbol)) + (when (looking-at-p " ") + (just-one-space)) (let* ((old-buffer-size (buffer-size)) (enc (sp-get-enclosing-sexp)) (inner-close (sp-get enc (delete-and-extract-region @@ -7018,7 +8251,9 @@ If the raw prefix is negative, this behaves as \\[universal-argument] `sp-backwa (setq b (region-beginning)) (setq e (region-end)) (goto-char (sp-get enc :end-in)) - (if (looking-back "\n[ \t]*") + (if (save-excursion + (skip-chars-backward "\t ") + (bolp)) (let ((whitespace (sp-get-whitespace))) (sp-get whitespace (when (= :beg e) (delete-region :beg :end)))) @@ -7055,41 +8290,44 @@ expressions up until the start of enclosing list." ;; out a way to make it smoother. (interactive "P") (sp-select-next-thing arg) - (let ((enc (sp-get-enclosing-sexp)) - (dws 0) ;length of deleted whitespace - save-text b e nl) - (save-excursion - (setq b (region-beginning)) - (setq e (region-end)) - (goto-char (sp-get enc :end-in)) - (if (looking-back "\n[ \t]*") + (sp--with-case-sensitive + (let ((enc (sp-get-enclosing-sexp)) + (dws 0) ;length of deleted whitespace + save-text b e nl) + (save-excursion + (setq b (region-beginning)) + (setq e (region-end)) + (goto-char (sp-get enc :end-in)) + (if (save-excursion + (skip-chars-backward "\t ") + (bolp)) + (let ((whitespace (sp-get-whitespace))) + (sp-get whitespace + (when (= :beg e) + (delete-region :beg :end) + (setq dws (- :end :beg))))) + (setq nl t)) + (setq save-text (delete-and-extract-region b e)) + (when nl (let ((whitespace (sp-get-whitespace))) - (sp-get whitespace - (when (= :beg e) - (delete-region :beg :end) - (setq dws (- :end :beg))))) - (setq nl t)) - (setq save-text (delete-and-extract-region b e)) - (when nl + (sp-get whitespace (delete-region :beg :end)) + (sp-get whitespace (setq dws (+ dws (- :end :beg)))))) + (sp-get enc (goto-char (- :end (length save-text) dws))) + (insert "\n" save-text) + (sp-get enc (sp--indent-region :beg-prf :end)) + (setq e (point))) + ;; if we're at an empty line, remove it + (setq dws 0) ; variable reuse, ugly :/ + (when (string-match-p "^[\n\t ]+\\'" (thing-at-point 'line)) + (let ((b (bounds-of-thing-at-point 'line))) + (delete-region (car b) (cdr b)) + (setq dws (- (cdr b) (car b))))) + (when (sp--looking-back (sp--get-opening-regexp) nil t) (let ((whitespace (sp-get-whitespace))) - (sp-get whitespace (delete-region :beg :end)) - (sp-get whitespace (setq dws (+ dws (- :end :beg)))))) - (sp-get enc (goto-char (- :end (length save-text) dws))) - (insert "\n" save-text) - (sp-get enc (sp--indent-region :beg-prf :end)) - (setq e (point))) - ;; if we're at an empty line, remove it - (setq dws 0) ; variable reuse, ugly :/ - (when (string-match-p "^[\n\t ]+\\'" (thing-at-point 'line)) - (let ((b (bounds-of-thing-at-point 'line))) - (delete-region (car b) (cdr b)) - (setq dws (- (cdr b) (car b))))) - (when (sp--looking-back (sp--get-opening-regexp) nil t) - (let ((whitespace (sp-get-whitespace))) - (sp-get whitespace - (delete-region :beg :end) - (setq dws (- :end :beg))))) - (goto-char (- e dws)))) + (sp-get whitespace + (delete-region :beg :end) + (setq dws (- :end :beg))))) + (goto-char (- e dws))))) (defun sp-forward-whitespace (&optional arg) "Skip forward past the whitespace characters. @@ -7257,7 +8495,6 @@ not necessarily represent a valid balanced expression!" (save-excursion (let* ((raw (sp--raw-argument-p arg)) (arg (prefix-numeric-value arg)) - (dir (sp--signum arg)) (beg point) (end point) (op "") (cl "") (prefix "") @@ -7415,6 +8652,10 @@ considered balanced expressions." (b (sp-get selection :beg)) (e (sp-get selection :end)) contracted) + ;; Show a helpful error if we're trying to move beyond the + ;; beginning or end of the buffer. + (when (or (null b) (null e)) + (user-error (if (bobp) "At beginning of buffer" "At end of buffer"))) ;; if region is active and ready to use, check if this selection ;; == old selection. If so, reselect the insides (when (region-active-p) @@ -7463,6 +8704,35 @@ considered balanced expressions." (sp-select-previous-thing arg point) (exchange-point-and-mark))) +(defun sp-mark-sexp (&optional arg allow-extend) + "Set mark ARG balanced expressions from point. +The place mark goes is the same place \\[sp-forward-sexp] would +move to with the same argument. +Interactively, if this command is repeated +or (in Transient Mark mode) if the mark is active, +it marks the next ARG sexps after the ones already marked. +This command assumes point is not in a string or comment." + (interactive "P\np") + (cond ((and allow-extend + (or (and (eq last-command this-command) (mark t)) + (and transient-mark-mode mark-active))) + (setq arg (if arg (prefix-numeric-value arg) + (if (< (mark) (point)) -1 1))) + (set-mark + (save-excursion + (let ((p (point))) + (goto-char (mark)) + (sp-forward-sexp arg) + (unless (sp-region-ok-p p (point)) + (user-error "Can not extend selection: region invalid")) + (point))))) + (t + (push-mark + (save-excursion + (sp-forward-sexp (prefix-numeric-value arg)) + (point)) + nil t)))) + (defun sp-delete-char (&optional arg) "Delete a character forward or move forward over a delimiter. @@ -7495,48 +8765,58 @@ Examples: |(foo bar) -> (|foo bar)" (interactive "P") - (let* ((raw (sp--raw-argument-p arg)) - ;; if you edit 10 gigabyte files in Emacs, you're gonna have - ;; a bad time. - (n (if raw 100000000 - (prefix-numeric-value arg)))) - (cond - ((> n 0) - (while (> n 0) - (cond - ((let ((ok (sp-point-in-empty-sexp))) - (when ok - (backward-char (length (car ok))) - (delete-char (+ (length (car ok)) (length (cdr ok))))) - ok) - ;; make this customizable - (setq n (1- n))) - ((and (sp-point-in-string) - (save-excursion (forward-char) (not (sp-point-in-string)))) - (setq n 0)) - ((sp--looking-at (sp--get-opening-regexp (sp--get-pair-list-context 'navigate))) - (if (save-match-data (sp-get-thing)) - (goto-char (match-end 0)) - (delete-char (length (match-string 0)))) - ;; make this customizable - (setq n (1- n))) - ((and (not (sp-point-in-string)) - (save-excursion (forward-char) (sp-point-in-string))) - (forward-char) - ;; make this customizable - (setq n (1- n))) - ((sp--looking-at (sp--get-closing-regexp (sp--get-pair-list-context 'navigate))) - (if (save-match-data (sp-get-thing)) - ;; make this customizable -- maybe we want to skip and - ;; continue deleting - (setq n 0) - (delete-char (length (match-string 0))) - (setq n (1- n)))) - (t - (delete-char 1) - (setq n (1- n)))))) - ((= n 0) (delete-char 1)) - (t (sp-backward-delete-char (sp--negate-argument arg)))))) + (sp--with-case-sensitive + (let* ((raw (sp--raw-argument-p arg)) + ;; if you edit 10 gigabyte files in Emacs, you're gonna have + ;; a bad time. + (n (if raw 100000000 + (prefix-numeric-value arg)))) + (cond + ((> n 0) + (while (> n 0) + (cond + ((let ((ok (sp-point-in-empty-sexp))) + (when ok + (backward-char (length (car ok))) + (delete-char (+ (length (car ok)) (length (cdr ok))))) + ok) + ;; make this customizable + (setq n (1- n))) + ((and (sp-point-in-string) + (save-excursion (forward-char) (not (sp-point-in-string)))) + (setq n 0)) + ((sp--looking-at (sp--get-opening-regexp (sp--get-pair-list-context 'navigate))) + (-if-let (thing (save-match-data (sp-get-thing))) + (cond + ((= (sp-get thing :end-in) (point)) + (setq n 0)) + ((= (sp-get thing :beg) (point)) + (goto-char (sp-get thing :beg-in))) + (t + (delete-char (length (match-string 0))))) + (delete-char (length (match-string 0)))) + ;; make this customizable + (setq n (1- n))) + ((and (not (sp-point-in-string)) + (save-excursion (forward-char) (sp-point-in-string))) + (forward-char) + ;; make this customizable + (setq n (1- n))) + ((sp--looking-at (sp--get-closing-regexp (sp--get-pair-list-context 'navigate))) + (if (save-match-data (sp-get-thing)) + ;; make this customizable -- maybe we want to skip and + ;; continue deleting + (setq n 0) + (delete-char (length (match-string 0))) + (setq n (1- n)))) + ((bound-and-true-p hungry-delete-mode) + (hungry-delete-forward 1) + (setq n (1- n))) + (t + (delete-char 1) + (setq n (1- n)))))) + ((= n 0) (delete-char 1)) + (t (sp-backward-delete-char (sp--negate-argument arg))))))) (defun sp-backward-delete-char (&optional arg) "Delete a character backward or move backward over a delimiter. @@ -7570,48 +8850,61 @@ Examples: (foo bar)| -> (foo bar|)" (interactive "P") - (let* ((raw (sp--raw-argument-p arg)) - ;; if you edit 10 gigabyte files in Emacs, you're gonna have - ;; a bad time. - (n (if raw 100000000 - (prefix-numeric-value arg)))) - (cond - ((> n 0) - (while (> n 0) + (if (and sp-autodelete-wrap + (eq sp-last-operation 'sp-wrap-region)) + (sp-backward-unwrap-sexp) + (sp--with-case-sensitive + (let* ((raw (sp--raw-argument-p arg)) + ;; if you edit 10 gigabyte files in Emacs, you're gonna have + ;; a bad time. + (n (if raw 100000000 + (prefix-numeric-value arg)))) (cond - ((let ((ok (sp-point-in-empty-sexp))) - (when ok - (backward-char (length (car ok))) - (delete-char (+ (length (car ok)) (length (cdr ok))))) - ok) - ;; make this customizable - (setq n (1- n))) - ((and (sp-point-in-string) - (save-excursion (backward-char) (not (sp-point-in-string)))) - (setq n 0)) - ((sp--looking-back (sp--get-closing-regexp (sp--get-pair-list-context 'navigate))) - (if (save-match-data (sp-get-thing t)) - (goto-char (match-beginning 0)) - (delete-char (- (length (match-string 0))))) - ;; make this customizable - (setq n (1- n))) - ((and (not (sp-point-in-string)) - (save-excursion (backward-char) (sp-point-in-string))) - (backward-char) - ;; make this customizable - (setq n (1- n))) - ((sp--looking-back (sp--get-opening-regexp (sp--get-pair-list-context 'navigate))) - (if (save-match-data (sp-get-thing t)) - ;; make this customizable -- maybe we want to skip and - ;; continue deleting - (setq n 0) - (delete-char (- (length (match-string 0)))) - (setq n (1- n)))) - (t - (delete-char -1) - (setq n (1- n)))))) - ((= n 0) (delete-char -1)) - (t (sp-delete-char (sp--negate-argument arg)))))) + ((> n 0) + (while (> n 0) + (cond + ((let ((ok (sp-point-in-empty-sexp))) + (when ok + (backward-char (length (car ok))) + (delete-char (+ (length (car ok)) (length (cdr ok))))) + ok) + ;; make this customizable + (setq n (1- n))) + ((and (sp-point-in-string) + (save-excursion (backward-char) (not (sp-point-in-string)))) + (setq n 0)) + ((sp--looking-back (sp--get-closing-regexp (sp--get-pair-list-context 'navigate))) + (-if-let (thing (save-match-data (sp-get-thing t))) + (cond + ((= (sp-get thing :end) (point)) + (goto-char (sp-get thing :end-in))) + ((= (sp-get thing :beg-in) (point)) + (setq n 0)) + (t + (delete-char (- (length (match-string 0)))))) + (delete-char (- (length (match-string 0))))) + ;; make this customizable + (setq n (1- n))) + ((and (not (sp-point-in-string)) + (save-excursion (backward-char) (sp-point-in-string))) + (backward-char) + ;; make this customizable + (setq n (1- n))) + ((sp--looking-back (sp--get-opening-regexp (sp--get-pair-list-context 'navigate))) + (if (save-match-data (sp-get-thing t)) + ;; make this customizable -- maybe we want to skip and + ;; continue deleting + (setq n 0) + (delete-char (- (length (match-string 0)))) + (setq n (1- n)))) + ((bound-and-true-p hungry-delete-mode) + (hungry-delete-backward 1) + (setq n (1- n))) + (t + (delete-char -1) + (setq n (1- n)))))) + ((= n 0) (delete-char -1)) + (t (sp-delete-char (sp--negate-argument arg)))))))) (put 'sp-backward-delete-char 'delete-selection 'supersede) (put 'sp-delete-char 'delete-selection 'supersede) @@ -7631,39 +8924,27 @@ delimiter enclosing this sexp." ((sp-point-in-empty-string pos))))) (defun sp-point-in-empty-string (&optional pos) - "Return non-nil if point is in empty sexp or string. + "Return non-nil if point is in empty string. The return value is actually cons pair of opening and closing string delimiter enclosing this string." (setq pos (or pos (point))) (when (and (sp-point-in-string) - (save-excursion (forward-char) (not (sp-point-in-string))) + (save-excursion (if (= (point-max) (point)) + t + (forward-char) (not (sp-point-in-string)))) (save-excursion (backward-char) (not (sp-point-in-string)))) (save-excursion (let* ((syntax (nth 3 (syntax-ppss pos))) (c (char-to-string (if (eq syntax t) (following-char) syntax)))) (cons c c))))) -(defun sp-zap-syntax (syntax &optional back) - "Delete characters forward until they match syntax class SYNTAX. - -If BACK is non-nil, delete backward." - (let ((p (point))) - (if back - (skip-syntax-backward syntax) - (skip-syntax-forward syntax)) - (delete-region p (point)))) - (defun sp--use-subword () "Return non-nil if word killing commands should kill subwords. This is the case if `subword-mode' is enabled and `sp-use-subword' is non-nil." (and sp-use-subword (bound-and-true-p subword-mode))) -(declare-function subword-kill "subword") -(declare-function subword-forward "subword") -(declare-function subword-backward "subword") - (defun sp--kill-word (&optional n) "Kill N words or subwords." (let ((n (or n 1))) @@ -7695,27 +8976,34 @@ backward direction. See `sp-forward-symbol' for what constitutes a symbol." (interactive "p") - (if (> arg 0) - (while (> arg 0) - (if (and word (sp-point-in-symbol)) - (sp--kill-word 1) - (let ((s (sp-get-symbol)) - (p (point))) - (when s - (sp-get s - (let ((delims (buffer-substring :beg-prf p))) - (if (string-match-p "\\`\\(\\s.\\|\\s-\\)*\\'" delims) - (if word - (kill-region p (save-excursion (sp--forward-word) (point))) - (kill-region p :end)) - (let ((kill-from (if (> p :beg-prf) :beg :beg-prf))) - (goto-char kill-from) - (if word - (kill-region kill-from (save-excursion (sp--forward-word) (point))) - (kill-region kill-from :end))))))))) - (sp--cleanup-after-kill) - (setq arg (1- arg))) - (sp-backward-kill-symbol (sp--negate-argument arg) word))) + (sp--with-case-sensitive + (if (> arg 0) + (while (> arg 0) + (-when-let (s (sp-get-symbol)) + (sp-get s + (let* ((beg + (if (< :beg-prf (point)) + (if word (point) :beg) + (if (= (save-excursion + (sp-skip-forward-to-symbol) + (point)) + :beg-prf) + (point) + :beg-prf))) + (end (if word + (let ((fw-end + (save-excursion + (sp--forward-word) + (point)))) + (if (sp-region-ok-p beg fw-end) + fw-end + :end-suf)) + :end-suf))) + (goto-char beg) + (kill-region beg end)))) + (sp--cleanup-after-kill) + (setq arg (1- arg))) + (sp-backward-kill-symbol (sp--negate-argument arg) word)))) (defun sp-kill-word (&optional arg) "Kill a word forward, skipping over intervening delimiters. @@ -7727,6 +9015,34 @@ backward direction." (interactive "p") (sp-kill-symbol arg t)) +(defun sp-delete-symbol (&optional arg word) + "Delete a symbol forward, skipping over any intervening delimiters. + +Deleted symbol does not go to the clipboard or kill ring. + +With ARG being positive number N, repeat that many times. + +With ARG being Negative number -N, repeat that many times in +backward direction. + +See `sp-forward-symbol' for what constitutes a symbol." + (interactive "p") + (let* ((kill-ring kill-ring) + (select-enable-clipboard nil)) + (sp-kill-symbol arg word))) + +(defun sp-delete-word (&optional arg) + "Delete a word forward, skipping over intervening delimiters. + +Deleted word does not go to the clipboard or kill ring. + +With ARG being positive number N, repeat that many times. + +With ARG being Negative number -N, repeat that many times in +backward direction." + (interactive "p") + (sp-delete-symbol arg t)) + (defun sp-backward-kill-symbol (&optional arg word) "Kill a symbol backward, skipping over any intervening delimiters. @@ -7737,26 +9053,34 @@ forward direction. See `sp-backward-symbol' for what constitutes a symbol." (interactive "p") - (if (> arg 0) - (while (> arg 0) - (if (and word (sp-point-in-symbol)) - (sp--kill-word -1) - (let ((s (sp-get-symbol t)) - (p (point))) - (when s - (sp-get s - (let ((delims (buffer-substring :end p))) - (if (string-match-p "\\`\\(\\s.\\|\\s-\\)*\\'" delims) - (if word - (kill-region (save-excursion (sp--backward-word) (point)) p) - (kill-region :beg-prf p)) - (goto-char :end) - (if word - (kill-region (save-excursion (sp--backward-word) (point)) :end) - (kill-region :beg-prf :end)))))))) - (sp--cleanup-after-kill) - (setq arg (1- arg))) - (sp-kill-symbol (sp--negate-argument arg) word))) + (sp--with-case-sensitive + (if (> arg 0) + (while (> arg 0) + (-when-let (s (sp-get-symbol t)) + (sp-get s + (let* ((end + (if (< (point) :end-suf) + (if word (point) :end-suf) + (if (= (save-excursion + (sp-skip-backward-to-symbol) + (point)) + :end-suf) + (point) + :end-suf))) + (beg (if word + (let ((bw-start + (save-excursion + (sp--backward-word) + (point)))) + (if (sp-region-ok-p bw-start end) + bw-start + :beg-prf)) + :beg-prf))) + (goto-char end) + (kill-region end beg)))) + (sp--cleanup-after-kill) + (setq arg (1- arg))) + (sp-kill-symbol (sp--negate-argument arg) word)))) (defun sp-backward-kill-word (&optional arg) "Kill a word backward, skipping over intervening delimiters. @@ -7768,6 +9092,62 @@ backward direction." (interactive "p") (sp-backward-kill-symbol arg t)) +(defun sp-backward-delete-symbol (&optional arg word) + "Delete a symbol backward, skipping over any intervening delimiters. + +Deleted symbol does not go to the clipboard or kill ring. + +With ARG being positive number N, repeat that many times. + +With ARG being Negative number -N, repeat that many times in +forward direction. + +See `sp-backward-symbol' for what constitutes a symbol." + (interactive "p") + (let* ((kill-ring kill-ring) + (select-enable-clipboard nil)) + (sp-backward-kill-symbol arg word))) + +(defun sp-backward-delete-word (&optional arg) + "Delete a word backward, skipping over intervening delimiters. + +Deleted word does not go to the clipboard or kill ring. + +With ARG being positive number N, repeat that many times. + +With ARG being Negative number -N, repeat that many times in +backward direction." + (interactive "p") + (sp-backward-delete-symbol arg t)) + +(defun sp-delete-region (beg end) + "Delete the text between point and mark, like `delete-region'. + +BEG and END are the bounds of region to be deleted. + +If that text is unbalanced, signal an error instead. +With a prefix argument, skip the balance check." + (interactive "r") + (when (or current-prefix-arg + (sp-region-ok-p beg end) + (user-error (sp-message :unbalanced-region :return))) + (setq this-command 'delete-region) + (delete-region beg end))) + +(defun sp-kill-region (beg end) + "Kill the text between point and mark, like `kill-region'. + +BEG and END are the bounds of region to be killed. + +If that text is unbalanced, signal an error instead. +With a prefix argument, skip the balance check." + (interactive "r") + (when (or current-prefix-arg + (sp-region-ok-p beg end) + (user-error (sp-message :unbalanced-region :return))) + (setq this-command 'kill-region) + (kill-region beg end))) + (defun sp-indent-defun (&optional arg) "Reindent the current defun. @@ -7787,41 +9167,28 @@ of the point." (indent-sexp)) (sp--back-to-indentation column indentation)))) -(defun sp--balanced-context-p (count start-context end-context) - (let ((string-or-comment-count (cl-first count)) - (normal-count (cl-second count))) - (cond - ((and start-context (eq start-context end-context)) - (zerop string-or-comment-count)) - ((eq start-context end-context) (zerop normal-count)) - (t (= string-or-comment-count normal-count 0))))) - (cl-defun sp-region-ok-p (start end) - (save-restriction - (save-excursion - (narrow-to-region start end) - (when (ignore-errors (scan-sexps (point-min) (point-max)) t) - (let ((count (list 0 0)) - (start-context (progn (goto-char start) (sp-point-in-string-or-comment))) - (end-context (progn (goto-char end) (sp-point-in-string-or-comment)))) - (dolist (pairs (sp--get-allowed-pair-list)) - (goto-char (point-min)) - (while (re-search-forward (sp--strict-regexp-quote (car pairs)) end :noerror) - (save-excursion - (backward-char) - (if (sp-point-in-string-or-comment) - (cl-incf (cl-first count)) - (cl-incf (cl-second count))))) - (goto-char (point-min)) - (while (re-search-forward (sp--strict-regexp-quote (cdr pairs)) end :noerror) - (save-excursion - (backward-char) - (if (sp-point-in-string-or-comment) - (cl-decf (cl-first count)) - (cl-decf (cl-second count))))) - (unless (sp--balanced-context-p count start-context end-context) - (cl-return-from sp-region-ok-p))) - t))))) + "Test if region between START and END is balanced. + +A balanced region is one where all opening delimiters are matched +by closing delimiters. + +This function does *not* check that the delimiters are correctly +ordered, that is [(]) is correct even though it is not logically +properly balanced." + (interactive "r") + (save-excursion + (save-restriction + (when (eq (sp-point-in-string start) (sp-point-in-string end)) + (narrow-to-region start end) + (let ((regex (sp--get-allowed-regexp (-difference sp-pair-list (sp--get-allowed-pair-list))))) + (goto-char (point-min)) + (while (or (prog1 (sp-forward-sexp) + (sp-skip-forward-to-symbol)) + ;; skip impossible delimiters + (when (looking-at-p regex) + (goto-char (match-end 0))))) + (looking-at-p "[[:blank:]\n]*\\'")))))) (defun sp-newline () "Insert a newline and indent it. @@ -7855,39 +9222,55 @@ comment." (if (= 1 (length (single-key-description last-command-event))) ;; pretty hacky (insert (single-key-description last-command-event)) (insert comment-start)) - (let ((old-point (point)) - (column (current-column)) - (indentation (sp--current-indentation)) - (old-line (line-number-at-pos)) - (hsexp (sp-get-hybrid-sexp)) - (newline-inserted 0)) - (goto-char (sp-get hsexp :end)) - (if (and (sp--looking-at-p (concat "\\s-*" (sp--get-closing-regexp))) - (= old-line (line-number-at-pos))) - (progn - (setq old-point (point)) + (sp--with-case-sensitive + (let ((old-point (point)) + (column (current-column)) + (indentation (sp--current-indentation)) + (old-line (line-number-at-pos)) + (hsexp (sp-get-hybrid-sexp)) + (newline-inserted 0)) + (goto-char (sp-get hsexp :end)) + (if (and (sp--looking-at-p (concat "\\s-*" (sp--get-closing-regexp))) + (= old-line (line-number-at-pos))) + (progn + (setq old-point (point)) + (newline) + (setq newline-inserted (1+ (- (line-end-position) (point))))) + (when (/= old-line (line-number-at-pos)) + (sp-backward-sexp) + (setq old-point (+ old-point (skip-syntax-backward " "))) (newline) - (setq newline-inserted (1+ (- (line-end-position) (point))))) - (when (/= old-line (line-number-at-pos)) - (sp-backward-sexp) - (setq old-point (+ old-point (skip-syntax-backward " "))) - (newline) - (setq newline-inserted (- (line-end-position) (point))))) - ;; @{ indenting madness - (goto-char old-point) - (sp-get hsexp (sp--indent-region :beg (+ :end newline-inserted))) - (sp--back-to-indentation column indentation) - ;; @} - (let ((comment-delim (or (cdr (--first (memq major-mode (car it)) sp-comment-string)) - comment-start))) - (when (and (/= 0 (current-column)) - (not (sp--looking-back-p "\\s-"))) - (insert " ")) - (insert comment-delim) - (when (/= newline-inserted 0) - (save-excursion - (forward-line 1) - (indent-according-to-mode))))))) + (setq newline-inserted (- (line-end-position) (point))))) + ;; @{ indenting madness + (goto-char old-point) + (sp-get hsexp (sp--indent-region :beg (+ :end newline-inserted))) + (sp--back-to-indentation column indentation) + ;; @} + (let ((comment-delim (or (cdr (--first (memq major-mode (car it)) sp-comment-string)) + comment-start))) + (when (and (/= 0 (current-column)) + (not (sp--looking-back-p "\\s-"))) + (insert " ")) + (insert comment-delim) + (when (/= newline-inserted 0) + (save-excursion + (forward-line 1) + (indent-according-to-mode)))))))) + +(defun sp-wrap-round () + "Wrap following sexp in round parentheses." + (interactive) + (sp-wrap-with-pair "(")) + +(defun sp-wrap-square () + "Wrap following sexp in square brackets." + (interactive) + (sp-wrap-with-pair "[")) + +(defun sp-wrap-curly () + "Wrap following sexp in curly braces." + (interactive) + (sp-wrap-with-pair "{")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -7936,10 +9319,19 @@ the opening delimiter or before the closing delimiter." "The face used to highlight pair overlays." :group 'show-smartparens) +(defface sp-show-pair-match-content-face + '() + "`show-smartparens-mode' face used for a matching pair's content." + :group 'show-smartparens) + (defvar sp-show-pair-idle-timer nil) (defvar sp-show-pair-overlays nil) +(defvar sp-show-pair-previous-match-positions nil) + +(defvar sp-show-pair-previous-point nil) + (defvar sp-show-pair-enc-overlays nil) ;;;###autoload @@ -7983,7 +9375,7 @@ support custom pairs." "Highlight the enclosing pair around point." (interactive)) -(defun sp-highlight-current-sexp (arg) +(defun sp-highlight-current-sexp (_arg) "Highlight the expression returned by the next command, preserving point position." (interactive "P") (let* ((cmd (read-key-sequence "" t)) @@ -7995,52 +9387,63 @@ support custom pairs." (execute-kbd-macro cmd)))) (defun sp-show--pair-function () - "Display the show pair overlays." + "Display the show pair overlays and print the line of the +matching paren in the echo area if not visible on screen." (when show-smartparens-mode - (save-match-data - (cl-labels ((scan-and-place-overlays - (match &optional back) - ;; we can use `sp-get-thing' here because we *are* at some - ;; pair opening, and so only the tag or the sexp can trigger. - (-if-let (ok (sp-get-thing back)) - (sp-get ok - (when (and (<= :beg (point)) (<= (point) :end)) - (sp-show--pair-create-overlays :beg :end :op-l :cl-l))) - (if back - (sp-show--pair-create-mismatch-overlay (- (point) (length match)) - (length match)) - (sp-show--pair-create-mismatch-overlay (point) (length match)))))) - (let* ((pair-list (sp--get-allowed-pair-list)) - (opening (sp--get-opening-regexp pair-list)) - (closing (sp--get-closing-regexp pair-list)) - (allowed (and sp-show-pair-from-inside (sp--get-allowed-regexp))) - match) - (cond - ;; if we are in a situation "()|", we should highlight the - ;; regular pair and not the string pair "from inside" - ((and (not (sp--evil-normal-state-p)) - (not (sp--evil-motion-state-p)) - (not (sp--evil-visual-state-p)) - (sp--looking-back (if sp-show-pair-from-inside allowed closing))) - (scan-and-place-overlays (match-string 0) :back)) - ((or (and (or (sp--evil-normal-state-p) - (sp--evil-motion-state-p) - (sp--evil-visual-state-p)) - (sp--looking-at (sp--get-allowed-regexp))) - (sp--looking-at (if sp-show-pair-from-inside allowed opening)) - (and (memq major-mode sp-navigate-consider-stringlike-sexp) - (looking-at (sp--get-stringlike-regexp))) - (and (memq major-mode sp-navigate-consider-sgml-tags) - (looking-at "<"))) - (scan-and-place-overlays (match-string 0))) - ((or (sp--looking-back (if sp-show-pair-from-inside allowed closing)) - (and (memq major-mode sp-navigate-consider-stringlike-sexp) - (sp--looking-back (sp--get-stringlike-regexp))) - (and (memq major-mode sp-navigate-consider-sgml-tags) - (sp--looking-back ">"))) - (scan-and-place-overlays (match-string 0) :back)) - (sp-show-pair-overlays - (sp-show--pair-delete-overlays)))))))) + (sp--with-case-sensitive + (save-match-data + (cl-labels ((scan-and-place-overlays + (match &optional back) + ;; we can use `sp-get-thing' here because we *are* at some + ;; pair opening, and so only the tag or the sexp can trigger. + (-if-let (ok (sp-get-thing back)) + (sp-get ok + (when (or (and back + (or (= :end (point)) + (= :beg-in (point)))) + (and (not back) + (or (= :beg (point)) + (= :end-in (point))))) + (sp-show--pair-create-overlays :beg :end :op-l :cl-l) + (when (and sp-echo-match-when-invisible + (not (or (active-minibuffer-window) cursor-in-echo-area))) + (sp-show--pair-echo-match :beg :end :op-l :cl-l)))) + (if back + (sp-show--pair-create-mismatch-overlay (- (point) (length match)) + (length match)) + (sp-show--pair-create-mismatch-overlay (point) (length match))) + (setq sp-show-pair-previous-match-positions nil) + (setq sp-show-pair-previous-point nil)))) + (let* ((pair-list (sp--get-allowed-pair-list)) + (opening (sp--get-opening-regexp pair-list)) + (closing (sp--get-closing-regexp pair-list)) + (allowed (and sp-show-pair-from-inside (sp--get-allowed-regexp)))) + (cond + ;; if we are in a situation "()|", we should highlight the + ;; regular pair and not the string pair "from inside" + ((and (not (sp--evil-normal-state-p)) + (not (sp--evil-motion-state-p)) + (not (sp--evil-visual-state-p)) + (sp--looking-back (if sp-show-pair-from-inside allowed closing))) + (scan-and-place-overlays (match-string 0) :back)) + ((or (and (or (sp--evil-normal-state-p) + (sp--evil-motion-state-p) + (sp--evil-visual-state-p)) + (sp--looking-at (sp--get-allowed-regexp))) + (sp--looking-at (if sp-show-pair-from-inside allowed opening)) + (looking-at (sp--get-stringlike-regexp)) + (and (memq major-mode sp-navigate-consider-sgml-tags) + (looking-at "<"))) + (scan-and-place-overlays (match-string 0))) + ((or (sp--looking-back (if sp-show-pair-from-inside allowed closing)) + (sp--looking-back (sp--get-stringlike-regexp)) + (and (memq major-mode sp-navigate-consider-sgml-tags) + (sp--looking-back ">"))) + (scan-and-place-overlays (match-string 0) :back)) + (sp-show-pair-overlays + (sp-show--pair-delete-overlays) + (setq sp-show-pair-previous-match-positions nil) + (setq sp-show-pair-previous-point nil))))))))) (defun sp-show--pair-enc-function (&optional thing) "Display the show pair overlays for enclosing expression." @@ -8053,14 +9456,53 @@ support custom pairs." (when sp-show-pair-overlays (sp-show--pair-delete-overlays)) (let* ((oleft (make-overlay start (+ start olen) nil t nil)) + (omiddle (make-overlay (+ start olen) (- end clen) nil t nil)) (oright (make-overlay (- end clen) end nil t nil))) - (setq sp-show-pair-overlays (cons oleft oright)) + (setq sp-show-pair-overlays (list oleft omiddle oright)) (overlay-put oleft 'face 'sp-show-pair-match-face) + (unless (use-region-p) + (overlay-put omiddle 'face 'sp-show-pair-match-content-face)) (overlay-put oright 'face 'sp-show-pair-match-face) (overlay-put oleft 'priority 1000) + (overlay-put omiddle 'priority 1000) (overlay-put oright 'priority 1000) (overlay-put oleft 'type 'show-pair))) +(defun sp-show--pair-echo-match (start end olen clen) + "Print the line of the matching paren in the echo area if not +visible on screen. Needs to be called after the show-pair overlay +has been created." + (let ((match-positions (list start end olen clen))) + (when (not (and (equal sp-show-pair-previous-match-positions match-positions) + (equal sp-show-pair-previous-point (point)))) + (setq sp-show-pair-previous-match-positions match-positions) + (setq sp-show-pair-previous-point (point)) + (let* ((visible-start (pos-visible-in-window-p start)) + (visible-end (pos-visible-in-window-p end)) + (where (cond + ((not visible-start) start) + ((not visible-end) end)))) + (when where + (save-excursion + (let* ((from (progn (goto-char where) (beginning-of-line) (point))) + (to (progn (end-of-line) (point))) + (line (buffer-substring from to)) + (message-log-max)) ;; don't log in messages + ;; Add smartparens overlay for opening parens + (let* ((i1 (- start from)) + (i2 (+ i1 olen))) + (when (and (< i1 (length line)) (>= i2 0)) + (add-face-text-property (max i1 0) (min i2 (length line)) + 'sp-show-pair-match-face nil line))) + ;; Add smartparens overlay for closing parens + (let* ((i1 (- end from 1)) + (i2 (+ i1 clen))) + (when (and (< i1 (length line)) (>= i2 0)) + (add-face-text-property (max i1 0) (min i2 (length line)) + 'sp-show-pair-match-face nil line))) + ;; echo line of match + (message "Matches: %s" (string-trim line))))))))) + (defun sp-show--pair-create-enc-overlays (start end olen clen) "Create the show pair enclosing overlays" (when sp-show-pair-enc-overlays @@ -8079,7 +9521,7 @@ support custom pairs." (when sp-show-pair-overlays (sp-show--pair-delete-overlays)) (let ((o (make-overlay start (+ start len) nil t nil))) - (setq sp-show-pair-overlays (cons o nil)) + (setq sp-show-pair-overlays (list o)) (overlay-put o 'face 'sp-show-pair-mismatch-face) (overlay-put o 'priority 1000) (overlay-put o 'type 'show-pair))) @@ -8087,10 +9529,8 @@ support custom pairs." (defun sp-show--pair-delete-overlays () "Remove both show pair overlays." (when sp-show-pair-overlays - (when (car sp-show-pair-overlays) - (delete-overlay (car sp-show-pair-overlays))) - (when (cdr sp-show-pair-overlays) - (delete-overlay (cdr sp-show-pair-overlays))) + (dolist (overlay sp-show-pair-overlays) + (delete-overlay overlay)) (setq sp-show-pair-overlays nil))) (defun sp-show--pair-delete-enc-overlays () @@ -8111,7 +9551,6 @@ support custom pairs." (save-match-data (sp-delete-pair (ad-get-arg 0)))) (add-hook 'post-command-hook 'sp--post-command-hook-handler) -(add-hook 'pre-command-hook 'sp--pre-command-hook-handler) (sp--set-base-key-bindings) (sp--update-override-key-bindings) -- 2.47.3