commit | author | age
|
5cb5f7
|
1 |
;;; smartparens-ess.el --- Smartparens Extension for Emacs Speaks Statistics -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (c) 2015-2016 Bernhard Pröll |
|
4 |
|
|
5 |
;; Author: Bernhard Pröll |
|
6 |
;; Maintainer: Bernhard Pröll |
|
7 |
;; URL: https://github.com/Fuco1/smartparens |
|
8 |
;; Created: 2015-02-26 |
|
9 |
;; Version: 0.2 |
|
10 |
;; Keywords: abbrev convenience editing |
|
11 |
|
|
12 |
;; This file is NOT part of GNU Emacs. |
|
13 |
|
|
14 |
;; This program is free software; you can redistribute it and/or modify |
|
15 |
;; it under the terms of the GNU General Public License as published by |
|
16 |
;; the Free Software Foundation, either version 3 of the License, or |
|
17 |
;; (at your option) any later version. |
|
18 |
|
|
19 |
;; This program is distributed in the hope that it will be useful, |
|
20 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
21 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
22 |
;; GNU General Public License for more details. |
|
23 |
|
|
24 |
;; You should have received a copy of the GNU General Public License |
|
25 |
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
26 |
|
|
27 |
;;; Commentary: |
|
28 |
;; |
|
29 |
;; This file provides some additional configuration for ESS. To use |
|
30 |
;; it, simply add: |
|
31 |
;; |
|
32 |
;; (require 'smartparens-ess) |
|
33 |
;; |
|
34 |
;; into your configuration. You can use this in conjunction with the |
|
35 |
;; default config or your own configuration. |
|
36 |
;; |
|
37 |
;;; Code: |
|
38 |
|
|
39 |
(require 'smartparens) |
|
40 |
(require 'rx) |
|
41 |
|
|
42 |
(defvar ess-roxy-str) |
|
43 |
|
|
44 |
(declare-function ess-roxy-indent-on-newline "ess-roxy") |
|
45 |
|
|
46 |
|
|
47 |
;; avoid traveling commas when slurping |
|
48 |
;; (|a, b), c ---> (|a, b, c) |
|
49 |
(dolist (mode '(ess-mode inferior-ess-mode)) |
|
50 |
(add-to-list 'sp-sexp-suffix (list mode 'regexp ""))) |
|
51 |
|
|
52 |
;; `sp-sexp-prefix' for ESS |
|
53 |
(add-to-list 'sp-sexp-prefix |
|
54 |
(list 'ess-mode 'regexp |
|
55 |
(rx (zero-or-more (or word (syntax symbol)))))) |
|
56 |
|
|
57 |
;; slurping follows Google's R style guide |
|
58 |
;; see https://google.github.io/styleguide/Rguide.xml |
|
59 |
(defun sp-ess-pre-handler (_id action _context) |
|
60 |
"Remove spaces before opening parenthesis in a function call. |
|
61 |
Remove redundant space around commas. |
|
62 |
ID, ACTION, CONTEXT." |
|
63 |
(when (equal action 'slurp-forward) |
|
64 |
(let ((sxp (sp-get-thing 'back))) |
|
65 |
(save-excursion |
|
66 |
(goto-char (sp-get sxp :beg-prf)) |
|
67 |
;; (|) x ---> (x) |
|
68 |
(when (looking-back (rx (syntax open-parenthesis) |
|
69 |
(one-or-more space)) nil) |
|
70 |
(cycle-spacing 0 nil 'single-shot)) |
|
71 |
(cond |
|
72 |
;; (|)if(cond) ---> (|if (cond)) |
|
73 |
((member (sp-get sxp :prefix) '("if" "for" "while")) |
|
74 |
(goto-char (sp-get sxp :beg)) |
|
75 |
(cycle-spacing 1 nil 'single-shot)) |
|
76 |
;; (|)v [,2] <- if(x > 1) ---> (v[,2] <- if (x > 1)) |
|
77 |
((and |
|
78 |
(member (sp-get sxp :op) '("[" "(")) |
|
79 |
(equal (sp-get sxp :prefix) "") |
|
80 |
(looking-back |
|
81 |
(rx (and (not-char "%" ",") |
|
82 |
(not (syntax close-parenthesis))) |
|
83 |
(one-or-more space)) nil) |
|
84 |
(not (member |
|
85 |
(save-excursion |
|
86 |
(sp-backward-sexp) |
|
87 |
(thing-at-point 'word 'noprop)) |
|
88 |
'("if" "for" "while")))) |
|
89 |
(cycle-spacing 0 nil 'single-shot)) |
|
90 |
;; (|[...])%in% ---> ([...] %in%|) |
|
91 |
((or (looking-at "%") (looking-back "%" nil)) |
|
92 |
(just-one-space)) |
|
93 |
;; (|)a , b, c ---> (|a, b, c) |
|
94 |
((looking-back |
|
95 |
(rx (zero-or-more space) "," (zero-or-more space)) |
|
96 |
(line-beginning-position) 'greedy) |
|
97 |
(replace-match ", ")))))) |
|
98 |
(when (equal action 'slurp-backward) |
|
99 |
(let ((sxp (sp-get-thing))) |
|
100 |
(save-excursion |
|
101 |
(goto-char (sp-get sxp :end)) |
|
102 |
;; x (|) ---> (x) |
|
103 |
(when (looking-at (rx (one-or-more space) |
|
104 |
(syntax close-parenthesis))) |
|
105 |
(cycle-spacing 0 nil 'single-shot)) |
|
106 |
;; if(cond){} (|) ---> (if (cond) {}|) |
|
107 |
(cond ((member (sp-get sxp :prefix) '("if" "for" "while")) |
|
108 |
(goto-char (sp-get sxp :beg)) |
|
109 |
(cycle-spacing 1 nil 'single-shot)) |
|
110 |
;; for style reasons there should be a space before curly |
|
111 |
;; brackets and binary operators |
|
112 |
((and (member (sp-get sxp :op) '("{" "%")) |
|
113 |
(not (looking-at (rx (syntax close-parenthesis))))) |
|
114 |
(cycle-spacing 1 nil 'single-shot)) |
|
115 |
;; v[2](|) ---> (v[2]|) |
|
116 |
((and |
|
117 |
(not (member (thing-at-point 'word 'noprop) |
|
118 |
'("if" "for" "while"))) |
|
119 |
(looking-at |
|
120 |
(rx (and (zero-or-more space) |
|
121 |
(not-char "{") |
|
122 |
(or (syntax close-parenthesis) |
|
123 |
(char "(") |
|
124 |
(char "[")))))) |
|
125 |
(cycle-spacing 0 nil 'single-shot)) |
|
126 |
;; 1 , 2 (|) ---> (1, 2) |
|
127 |
((looking-at |
|
128 |
(rx (zero-or-more space) "," (zero-or-more space))) |
|
129 |
(replace-match ", "))))))) |
|
130 |
|
|
131 |
;; function(x) {|} ---> function(x) {\n|\n} |
|
132 |
;; ##' \tabular{rrr}{|} ---> |
|
133 |
;; ##' \tabular{rrr}{ |
|
134 |
;; ##' | |
|
135 |
;; ##' } |
|
136 |
(defun sp-ess-open-sexp-indent (&rest _args) |
|
137 |
"Open new brace or bracket with indentation. |
|
138 |
ARGS." |
|
139 |
(if (and (fboundp 'ess-roxy-entry-p) (ess-roxy-entry-p)) |
|
140 |
(progn |
|
141 |
(save-excursion (ess-roxy-indent-on-newline)) |
|
142 |
(when (looking-back ess-roxy-str nil) |
|
143 |
(cycle-spacing 3 nil t))) |
|
144 |
(newline) |
|
145 |
(indent-according-to-mode) |
|
146 |
(forward-line -1) |
|
147 |
(indent-according-to-mode))) |
|
148 |
|
|
149 |
(defun sp-ess-roxy-str-p (_id action _context) |
|
150 |
"Test if looking back at `ess-roxy-re'. |
|
151 |
ID, ACTION, CONTEXT." |
|
152 |
(when (and (boundp 'ess-roxy-re) (eq action 'insert)) |
|
153 |
(sp--looking-back-p ess-roxy-re))) |
|
154 |
|
|
155 |
(sp-with-modes 'ess-mode |
|
156 |
(sp-local-pair "{" nil |
|
157 |
:pre-handlers '(sp-ess-pre-handler) |
|
158 |
;; the more reasonable C-j interferes with default binding for |
|
159 |
;; `ess-eval-line' |
|
160 |
:post-handlers '((sp-ess-open-sexp-indent "M-j"))) |
|
161 |
(sp-local-pair "(" nil |
|
162 |
:pre-handlers '(sp-ess-pre-handler) |
|
163 |
:post-handlers '((sp-ess-open-sexp-indent "M-j"))) |
|
164 |
(sp-local-pair "[" nil |
|
165 |
:pre-handlers '(sp-ess-pre-handler) |
|
166 |
:post-handlers '((sp-ess-open-sexp-indent "M-j"))) |
|
167 |
(sp-local-pair "'" nil |
|
168 |
:unless '(sp-ess-roxy-str-p sp-in-comment-p sp-in-string-quotes-p))) |
|
169 |
|
|
170 |
;;; roxygen2 markup |
|
171 |
;; see https://cran.r-project.org/web/packages/roxygen2/vignettes/formatting.html |
|
172 |
(sp-with-modes 'ess-mode |
|
173 |
(sp-local-pair "\\strong{" "}" |
|
174 |
:when '(sp-in-comment-p) |
|
175 |
:trigger "\\strong") |
|
176 |
(sp-local-pair "\\emph{" "}" |
|
177 |
:when '(sp-in-comment-p) |
|
178 |
:trigger "\\emph") |
|
179 |
(sp-local-pair "\\code{" "}" |
|
180 |
:when '(sp-in-comment-p) |
|
181 |
:trigger "\\code") |
|
182 |
(sp-local-pair "\\url{" "}" |
|
183 |
:when '(sp-in-comment-p) |
|
184 |
:trigger "\\url") |
|
185 |
(sp-local-pair "\\link{" "}" |
|
186 |
:when '(sp-in-comment-p) |
|
187 |
:trigger "\\link") |
|
188 |
(sp-local-pair "\\href{" "}" |
|
189 |
:when '(sp-in-comment-p) |
|
190 |
:trigger "\\href" |
|
191 |
:suffix "{[^}]*}") |
|
192 |
(sp-local-pair "\\email{" "}" |
|
193 |
:when '(sp-in-comment-p) |
|
194 |
:trigger "\\email") |
|
195 |
(sp-local-pair "\\pkg{" "}" |
|
196 |
:when '(sp-in-comment-p) |
|
197 |
:trigger "\\pkg") |
|
198 |
(sp-local-pair "\\item{" "}" |
|
199 |
:when '(sp-in-comment-p) |
|
200 |
:post-handlers '((sp-ess-open-sexp-indent "M-j")) |
|
201 |
:trigger "\\item{") |
|
202 |
(sp-local-pair "\\enumerate{" "}" |
|
203 |
:when '(sp-in-comment-p) |
|
204 |
:post-handlers '((sp-ess-open-sexp-indent "M-j")) |
|
205 |
:trigger "\\enumerate") |
|
206 |
(sp-local-pair "\\itemize{" "}" |
|
207 |
:when '(sp-in-comment-p) |
|
208 |
:post-handlers '((sp-ess-open-sexp-indent "M-j")) |
|
209 |
:trigger "\\itemize") |
|
210 |
(sp-local-pair "\\describe{" "}" |
|
211 |
:when '(sp-in-comment-p) |
|
212 |
:post-handlers '((sp-ess-open-sexp-indent "M-j")) |
|
213 |
:trigger "\\describe") |
|
214 |
(sp-local-pair "\\eqn{" "}" |
|
215 |
:when '(sp-in-comment-p) |
|
216 |
:trigger "\\eqn") |
|
217 |
(sp-local-pair "\\deqn{" "}" |
|
218 |
:when '(sp-in-comment-p) |
|
219 |
:trigger "\\deqn") |
|
220 |
(sp-local-pair "\\tabular{" "}" |
|
221 |
:when '(sp-in-comment-p) |
|
222 |
:trigger "\\tabular" |
|
223 |
:post-handlers '((sp-ess-open-sexp-indent "M-j")) |
|
224 |
:suffix "{[^}]*}")) |
|
225 |
|
|
226 |
|
|
227 |
(provide 'smartparens-ess) |
|
228 |
;;; smartparens-ess ends here |