6 | ;; Version: 1.3
7 | ;; Package-Requires: ((ess "18.10.3"))
8 | ;; URL: https://github.com/lionel-/codegrip/tree/main/inst/emacs
9 |
10 | ;;; Commentary:
11 | ;; Provides interactive commands for reshaping code.
12 |
13 | (defvar codegrip--scratch-file nil)
14 |
15 | ;;;###autoload
16 | (defun codegrip-reshape ()
17 | (interactive)
18 | (inferior-ess-r-force)
19 | (codegrip--update-scratch)
20 | (let ((cmd (format "codegrip:::emacs_reshape(%d, %d, file = '%s')\n"
21 | (line-number-at-pos)
22 | (1+ (current-column))
23 | (codegrip--scratch-file))))
24 | (when (ess-boolean-command cmd)
25 | (let* ((out (read (codegrip--scratch-buffer-string)))
26 | (reshaped (car (plist-get out :reshaped)))
27 | (beg (codegrip--as-position (plist-get out :start)))
28 | (end (codegrip--as-position (plist-get out :end)))
29 | (point (point)))
30 | (kill-region beg end)
31 | (goto-char beg)
32 | (insert reshaped)
33 | (goto-char point)))))
34 |
35 | ;;;###autoload
36 | (defun codegrip-move-outside ()
37 | (interactive)
38 | (codegrip--move "codegrip:::emacs_move('outside', %d, %d, file = '%s')\n"))
39 |
40 | ;;;###autoload
41 | (defun codegrip-move-inside ()
42 | (interactive)
43 | (codegrip--move "codegrip:::emacs_move('inside', %d, %d, file = '%s')\n"))
44 |
45 | ;;;###autoload
46 | (defun codegrip-move-next ()
47 | (interactive)
48 | (codegrip--move "codegrip:::emacs_move('next', %d, %d, file = '%s')\n"))
49 |
50 | ;;;###autoload
51 | (defun codegrip-move-previous ()
52 | (interactive)
53 | (codegrip--move "codegrip:::emacs_move('previous', %d, %d, file = '%s')\n"))
54 |
55 | (defun codegrip--move (cmd)
56 | (interactive)
57 | (inferior-ess-r-force)
58 | (codegrip--update-scratch)
59 | (let ((cmd (format cmd
60 | (line-number-at-pos)
61 | (1+ (current-column))
62 | (codegrip--scratch-file))))
63 | (when (ess-boolean-command cmd)
64 | (let* ((out (read (codegrip--scratch-buffer-string)))
65 | (pos (codegrip--as-position out)))
66 | (goto-char pos)))))
67 |
68 | (defun codegrip--update-scratch ()
69 | (let ((buf (current-buffer)))
70 | (with-current-buffer (codegrip--scratch-buffer)
71 | (replace-buffer-contents buf)
72 | (basic-save-buffer)
73 | (kill-buffer))))
74 |
75 | (defun codegrip--scratch-file ()
76 | (unless codegrip--scratch-file
77 | (setq codegrip--scratch-file (make-temp-file "codegrip-scratch")))
78 | codegrip--scratch-file)
79 |
80 | (defun codegrip--scratch-buffer ()
81 | (let ((buf (find-file-noselect (codegrip--scratch-file))))
82 | (with-current-buffer buf
83 | (rename-buffer " *codegrip--scratch*"))
84 | buf))
85 |
86 | (defun codegrip--scratch-buffer-string ()
87 | (let* ((buf (codegrip--scratch-buffer))
88 | (out (with-current-buffer buf
89 | (buffer-string))))
90 | (kill-buffer buf)
91 | out))
92 |
93 | (defun codegrip--as-position (data)
94 | (save-excursion
95 | (codegrip--goto-line (plist-get data :line))
96 | (forward-char (1- (plist-get data :col)))
97 | (point)))
98 |
99 | (defun codegrip--goto-line (line)
100 | (goto-char (point-min))
101 | (forward-line (1- line)))
102 |
103 | (provide 'codegrip)
104 |
--------------------------------------------------------------------------------
/inst/rstudio/addins.dcf:
--------------------------------------------------------------------------------
1 | Name: Move Outside
2 | Description: Move to containing expression.
3 | Binding: addin_move_outside
4 | Interactive: false
5 |
6 | Name: Move Inside
7 | Description: Move inside expression.
8 | Binding: addin_move_inside
9 | Interactive: false
10 |
11 | Name: Reshape Expression
12 | Description: Reshape expressions longer or wider.
13 | Binding: addin_reshape
14 | Interactive: false
15 |
--------------------------------------------------------------------------------
/man/addin_reshape.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/reshape-addin.R
3 | \name{addin_reshape}
4 | \alias{addin_reshape}
5 | \title{Reshape expressions longer or wider}
6 | \usage{
7 | addin_reshape()
8 | }
9 | \description{
10 | \code{addin_reshape()} lets you cycle between different shapes of
11 | function calls. For instance, reshaping transforms code from wide to long
12 | shape and vice versa:
13 |
14 | \if{html}{\out{}}\preformatted{list(a, b, c)
15 |
16 | list(
17 | a,
18 | b,
19 | c
20 | )
21 | }\if{html}{\out{
}}
22 |
23 | Note that for function definitions, \code{addin_reshape()} cycles through two
24 | different long shapes. The traditional L form uses more horizontal space
25 | whereas the flat form uses less horizontal space and the arguments are
26 | always aligned at single indent:
27 |
28 | \if{html}{\out{}}\preformatted{foo <- function(a, b, c) \{
29 | NULL
30 | \}
31 |
32 | foo <- function(a,
33 | b,
34 | c) \{
35 | NULL
36 | \}
37 |
38 | foo <- function(
39 | a,
40 | b,
41 | c
42 | ) \{
43 | NULL
44 | \}
45 | }\if{html}{\out{
}}
46 | }
47 |
--------------------------------------------------------------------------------
/man/codegrip-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/codegrip-package.R
3 | \docType{package}
4 | \name{codegrip-package}
5 | \alias{codegrip}
6 | \alias{codegrip-package}
7 | \title{codegrip: Syntax-Based Editing and Navigation of R Code}
8 | \description{
9 | Provides addins for reshaping R code and navigating across syntactic constructs.
10 | }
11 | \seealso{
12 | Useful links:
13 | \itemize{
14 | \item \url{https://github.com/lionel-/codegrip}
15 | \item Report bugs at \url{https://github.com/lionel-/codegrip/issues}
16 | }
17 |
18 | }
19 | \author{
20 | \strong{Maintainer}: Lionel Henry \email{lionel@posit.co}
21 |
22 | Other contributors:
23 | \itemize{
24 | \item Posit PBC [copyright holder, funder]
25 | }
26 |
27 | }
28 | \keyword{internal}
29 |
--------------------------------------------------------------------------------
/man/figures/README/move-reshape.svg:
--------------------------------------------------------------------------------
1 | foo <- function() { list(a = 1, b = c(2, 3)) } list(a = 1, b = c( 2, 3 )) list( a = 1, b = c( 2, 3 ) )
--------------------------------------------------------------------------------
/man/figures/README/move.svg:
--------------------------------------------------------------------------------
1 | list(a = 1, b = c(2, 3))
--------------------------------------------------------------------------------
/man/figures/README/reshape-call.svg:
--------------------------------------------------------------------------------
1 | list(a, b, c) list( a, b, c )
--------------------------------------------------------------------------------
/man/figures/README/reshape-def.svg:
--------------------------------------------------------------------------------
1 | foo <- function(a, b, c) { NULL } foo <- function(a, b, c) { foo <- function( a, b, c ) {
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | # This file is part of the standard setup for testthat.
2 | # It is recommended that you do not modify it.
3 | #
4 | # Where should you do additional test configuration?
5 | # Learn more about the roles of various files in:
6 | # * https://r-pkgs.org/tests.html
7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files
8 |
9 | library(testthat)
10 | library(codegrip)
11 |
12 | test_check("codegrip")
13 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/ast-call.md:
--------------------------------------------------------------------------------
1 | # can find function call node for position
2 |
3 | Code
4 | # Node locations of function calls for all combinations of line and col
5 | call_nodes
6 | Output
7 | [[1]]
8 | [1] 0 0 0 0 0 0 0 0 0
9 |
10 | [[2]]
11 | [1] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1
12 |
13 | [[3]]
14 | [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
15 |
16 | [[4]]
17 | [1] 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 2 2 2 2 1 1 1 1 1 1
18 |
19 | [[5]]
20 | [1] 1 1 4 4 4 4 4 4 4 4
21 |
22 | [[6]]
23 | [1] 4 4 4 4
24 |
25 | [[7]]
26 | [1] 1 1 1
27 |
28 | [[8]]
29 | integer(0)
30 |
31 | [[9]]
32 | integer(0)
33 |
34 | [[10]]
35 | [1] 5 5 5 5
36 |
37 | [[11]]
38 | [1] 5 5 5 5 5
39 |
40 | [[12]]
41 | [1] 5
42 |
43 |
44 | ---
45 |
46 | Code
47 | # Positions of function call at 4:4
48 | node_positions(node)[1:4]
49 | Output
50 | line1 col1 line2 col2
51 | 1 4 3 4 21
52 |
53 | # can retrieve function call text
54 |
55 | Code
56 | # Cursor on `function`
57 | node <- find_function_call(2, 13, data = xml)
58 | cat_line(node_text(node, info = info))
59 | Output
60 | function(bar,
61 | baz) {
62 | quux(1, list(2), 3) # foo
63 | (foo)(4,
64 | 5)
65 | }
66 | Code
67 | # Cursor on `quux`
68 | node <- find_function_call(4, 4, data = xml)
69 | cat_line(node_text(node, info = info))
70 | Output
71 | quux(1, list(2), 3)
72 | Code
73 | # Cursor on complex call
74 | node <- find_function_call(5, 3, data = xml)
75 | cat_line(node_text(node, info = info))
76 | Output
77 | (foo)(4,
78 | 5)
79 | Code
80 | # Cursor on `hop`
81 | node <- find_function_call(11, 1, data = xml)
82 | cat_line(node_text(node, info = info))
83 | Output
84 | hop(
85 | hip
86 | )
87 |
88 | # check_call() detects calls
89 |
90 | Code
91 | (expect_error(fn(expr)))
92 | Output
93 |
94 | Error in `fn()`:
95 | ! `x` must be a function call node.
96 |
97 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/reshape-call.md:
--------------------------------------------------------------------------------
1 | # can reshape call longer
2 |
3 | Code
4 | print_longer("()")
5 | Output
6 | foofybaz()
7 |
8 | function() NULL
9 | Code
10 | print_longer("(a)")
11 | Output
12 | foofybaz(
13 | a
14 | )
15 |
16 | function(
17 | a
18 | ) NULL
19 | Code
20 | print_longer("(b, c)")
21 | Output
22 | foofybaz(
23 | b,
24 | c
25 | )
26 |
27 | function(
28 | b,
29 | c
30 | ) NULL
31 | Code
32 | print_longer("(a, b, c)")
33 | Output
34 | foofybaz(
35 | a,
36 | b,
37 | c
38 | )
39 |
40 | function(
41 | a,
42 | b,
43 | c
44 | ) NULL
45 | Code
46 | print_longer("(a = 1, b, c = 3)")
47 | Output
48 | foofybaz(
49 | a = 1,
50 | b,
51 | c = 3
52 | )
53 |
54 | function(
55 | a = 1,
56 | b,
57 | c = 3
58 | ) NULL
59 | Code
60 | # Leading indentation is preserved. First line is not indented
61 | # because the reshaped text is meant to be inserted at the node
62 | # coordinates.
63 | print_longer(" ()")
64 | Output
65 | foofybaz()
66 |
67 | function() NULL
68 | Code
69 | print_longer(" (a)")
70 | Output
71 | foofybaz(
72 | a
73 | )
74 |
75 | function(
76 | a
77 | ) NULL
78 | Code
79 | print_longer(" (a, b)")
80 | Output
81 | foofybaz(
82 | a,
83 | b
84 | )
85 |
86 | function(
87 | a,
88 | b
89 | ) NULL
90 | Code
91 | # Multiline args are indented as is
92 | print_longer("(a, b = foo(\n bar\n), c)")
93 | Output
94 | foofybaz(
95 | a,
96 | b = foo(
97 | bar
98 | ),
99 | c
100 | )
101 |
102 | function(
103 | a,
104 | b = foo(
105 | bar
106 | ),
107 | c
108 | ) NULL
109 | Code
110 | print_longer("(a, b =\n 2, c)")
111 | Output
112 | foofybaz(
113 | a,
114 | b =
115 | 2,
116 | c
117 | )
118 |
119 | function(
120 | a,
121 | b =
122 | 2,
123 | c
124 | ) NULL
125 | Code
126 | print_longer(" (a, b = foo(\n bar \n ), c)")
127 | Output
128 | foofybaz(
129 | a,
130 | b = foo(
131 | bar
132 | ),
133 | c
134 | )
135 |
136 | function(
137 | a,
138 | b = foo(
139 | bar
140 | ),
141 | c
142 | ) NULL
143 | Code
144 | # Wrong indentation is preserved
145 | print_longer("(a, b = foo(\nbar\n), c)")
146 | Output
147 | foofybaz(
148 | a,
149 | b = foo(
150 | bar
151 | ),
152 | c
153 | )
154 |
155 | function(
156 | a,
157 | b = foo(
158 | bar
159 | ),
160 | c
161 | ) NULL
162 | Code
163 | print_longer(" (a, b = foo(\n bar\n), c)")
164 | Output
165 | foofybaz(
166 | a,
167 | b = foo(
168 | bar
169 | ),
170 | c
171 | )
172 |
173 | function(
174 | a,
175 | b = foo(
176 | bar
177 | ),
178 | c
179 | ) NULL
180 |
181 | # can reshape call longer (L shape)
182 |
183 | Code
184 | print_longer_l("()")
185 | Output
186 | foofybaz()
187 |
188 | function() NULL
189 | Code
190 | print_longer_l("(a)")
191 | Output
192 | foofybaz(a)
193 |
194 | function(a) NULL
195 | Code
196 | print_longer_l("(a, b)")
197 | Output
198 | foofybaz(a,
199 | b)
200 |
201 | function(a,
202 | b) NULL
203 | Code
204 | print_longer_l("(a, b, c)")
205 | Output
206 | foofybaz(a,
207 | b,
208 | c)
209 |
210 | function(a,
211 | b,
212 | c) NULL
213 | Code
214 | print_longer_l("(a = 1, b, c = 3)")
215 | Output
216 | foofybaz(a = 1,
217 | b,
218 | c = 3)
219 |
220 | function(a = 1,
221 | b,
222 | c = 3) NULL
223 | Code
224 | # Leading indentation is preserved. First line is not indented
225 | # because the reshaped text is meant to be inserted at the node
226 | # coordinates.
227 | print_longer_l(" ()")
228 | Output
229 | foofybaz()
230 |
231 | function() NULL
232 | Code
233 | print_longer_l(" (a)")
234 | Output
235 | foofybaz(a)
236 |
237 | function(a) NULL
238 | Code
239 | print_longer_l(" (a, b)")
240 | Output
241 | foofybaz(a,
242 | b)
243 |
244 | function(a,
245 | b) NULL
246 | Code
247 | # Multiline args are indented as is
248 | print_longer_l("(a, b = foo(\n bar\n), c)")
249 | Output
250 | foofybaz(a,
251 | b = foo(
252 | bar
253 | ),
254 | c)
255 |
256 | function(a,
257 | b = foo(
258 | bar
259 | ),
260 | c) NULL
261 | Code
262 | print_longer_l("(a, b =\n 2, c)")
263 | Output
264 | foofybaz(a,
265 | b =
266 | 2,
267 | c)
268 |
269 | function(a,
270 | b =
271 | 2,
272 | c) NULL
273 | Code
274 | print_longer_l(" (a, b = foo(\n bar \n ), c)")
275 | Output
276 | foofybaz(a,
277 | b = foo(
278 | bar
279 | ),
280 | c)
281 |
282 | function(a,
283 | b = foo(
284 | bar
285 | ),
286 | c) NULL
287 | Code
288 | # Wrong indentation is preserved
289 | print_longer_l("(a, b = foo(\nbar\n), c)")
290 | Output
291 | foofybaz(a,
292 | b = foo(
293 | bar
294 | ),
295 | c)
296 |
297 | function(a,
298 | b = foo(
299 | bar
300 | ),
301 | c) NULL
302 | Code
303 | print_longer_l(" (a, b = foo(\n bar\n), c)")
304 | Output
305 | foofybaz(a,
306 | b = foo(
307 | bar
308 | ),
309 | c)
310 |
311 | function(a,
312 | b = foo(
313 | bar
314 | ),
315 | c) NULL
316 |
317 | # can reshape call wider
318 |
319 | Code
320 | print_wider("()")
321 | Output
322 | foofybaz()
323 |
324 | function() NULL
325 | Code
326 | print_wider("(\n a\n)")
327 | Output
328 | foofybaz(a)
329 |
330 | function(a) NULL
331 | Code
332 | print_wider("(\n\n a\n\n)")
333 | Output
334 | foofybaz(a)
335 |
336 | function(a) NULL
337 | Code
338 | print_wider("(\n a, \n b\n)")
339 | Output
340 | foofybaz(a, b)
341 |
342 | function(a, b) NULL
343 | Code
344 | print_wider("(\n a, \n b, \n c\n)")
345 | Output
346 | foofybaz(a, b, c)
347 |
348 | function(a, b, c) NULL
349 | Code
350 | print_wider("(\n a = 1,\n b,\n c = 3\n)")
351 | Output
352 | foofybaz(a = 1, b, c = 3)
353 |
354 | function(a = 1, b, c = 3) NULL
355 | Code
356 | # Leading indentation is ignored
357 | print_wider(" ()")
358 | Output
359 | foofybaz()
360 |
361 | function() NULL
362 | Code
363 | print_wider(" (\n a\n)")
364 | Output
365 | foofybaz(a)
366 |
367 | function(a) NULL
368 | Code
369 | print_wider(" (\n\n a\n\n,\n b)")
370 | Output
371 | foofybaz(a, b)
372 |
373 | function(a, b) NULL
374 | Code
375 | # Multiline args are indented as is
376 | print_wider("(\n a,\n b = foo(\n bar\n ),\n c)")
377 | Output
378 | foofybaz(a, b = foo(
379 | bar
380 | ), c)
381 |
382 | function(a, b = foo(
383 | bar
384 | ), c) NULL
385 | Code
386 | print_wider("(\n a,\n b =\n 2,\n c\n)")
387 | Output
388 | foofybaz(a, b =
389 | 2, c)
390 |
391 | function(a, b =
392 | 2, c) NULL
393 |
394 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/reshape.md:
--------------------------------------------------------------------------------
1 | # reshape() cycles function calls and definitions
2 |
3 | Code
4 | code <- "foofybaz()"
5 | snap_reshape_cycle(2, code)
6 | Output
7 | i: 1
8 | foofybaz()
9 |
10 | i: 2
11 | foofybaz()
12 |
13 | Code
14 | code <- "foofybaz(a)"
15 | snap_reshape_cycle(3, code)
16 | Output
17 | i: 1
18 | foofybaz(
19 | a
20 | )
21 |
22 | i: 2
23 | foofybaz(a)
24 |
25 | i: 3
26 | foofybaz(
27 | a
28 | )
29 |
30 | Code
31 | code <- "foofybaz(a, b = 1, c)"
32 | snap_reshape_cycle(3, code)
33 | Output
34 | i: 1
35 | foofybaz(
36 | a,
37 | b = 1,
38 | c
39 | )
40 |
41 | i: 2
42 | foofybaz(a, b = 1, c)
43 |
44 | i: 3
45 | foofybaz(
46 | a,
47 | b = 1,
48 | c
49 | )
50 |
51 | Code
52 | code <- "function() NULL"
53 | snap_reshape_cycle(2, code)
54 | Output
55 | i: 1
56 | function() NULL
57 |
58 | i: 2
59 | function() NULL
60 |
61 | Code
62 | code <- "function(a) NULL"
63 | snap_reshape_cycle(3, code)
64 | Output
65 | i: 1
66 | function(
67 | a
68 | ) NULL
69 |
70 | i: 2
71 | function(a) NULL
72 |
73 | i: 3
74 | function(
75 | a
76 | ) NULL
77 |
78 | Code
79 | code <- "function(a, b = 1, c) NULL"
80 | snap_reshape_cycle(4, code)
81 | Output
82 | i: 1
83 | function(a,
84 | b = 1,
85 | c) NULL
86 |
87 | i: 2
88 | function(
89 | a,
90 | b = 1,
91 | c
92 | ) NULL
93 |
94 | i: 3
95 | function(a, b = 1, c) NULL
96 |
97 | i: 4
98 | function(a,
99 | b = 1,
100 | c) NULL
101 |
102 |
103 | # reshape() cycles other call-like constructs
104 |
105 | Code
106 | code <- "if (a) NULL"
107 | snap_reshape_cycle(2, code)
108 | Output
109 | i: 1
110 | if (
111 | a
112 | ) NULL
113 |
114 | i: 2
115 | if (a) NULL
116 |
117 | Code
118 | code <- "if (a) b else c"
119 | snap_reshape_cycle(2, code)
120 | Output
121 | i: 1
122 | if (
123 | a
124 | ) b else c
125 |
126 | i: 2
127 | if (a) b else c
128 |
129 | Code
130 | code <- "while (a) NULL"
131 | snap_reshape_cycle(2, code)
132 | Output
133 | i: 1
134 | while (
135 | a
136 | ) NULL
137 |
138 | i: 2
139 | while (a) NULL
140 |
141 | Code
142 | code <- "for (i in x) NULL"
143 | snap_reshape_cycle(1, code)
144 | Output
145 | i: 1
146 | for (i in x) NULL
147 |
148 |
149 | # can reshape braced expressions
150 |
151 | Code
152 | code <- "expect_snapshot({\n a\n b\n})"
153 | snap_reshape_cycle(2, code)
154 | Output
155 | i: 1
156 | expect_snapshot(
157 | {
158 | a
159 | b
160 | }
161 | )
162 |
163 | i: 2
164 | expect_snapshot({
165 | a
166 | b
167 | })
168 |
169 | Code
170 | code <- "{\n expect_snapshot({\n a\n b\n })\n}"
171 | snap_reshape_cycle(2, code, line = 2, col = 3)
172 | Output
173 | i: 1
174 | expect_snapshot(
175 | {
176 | a
177 | b
178 | }
179 | )
180 |
181 | i: 2
182 | expect_snapshot({
183 | a
184 | b
185 | })
186 |
187 | Code
188 | code <- "test_that('desc', {\n a\n b\n})"
189 | snap_reshape_cycle(3, code)
190 | Output
191 | i: 1
192 | test_that(
193 | 'desc',
194 | {
195 | a
196 | b
197 | }
198 | )
199 |
200 | i: 2
201 | test_that('desc', {
202 | a
203 | b
204 | })
205 |
206 | i: 3
207 | test_that(
208 | 'desc',
209 | {
210 | a
211 | b
212 | }
213 | )
214 |
215 | Code
216 | code <- "test_that({\n a\n b\n}, desc = 'desc')"
217 | snap_reshape_cycle(3, code)
218 | Output
219 | i: 1
220 | test_that(
221 | {
222 | a
223 | b
224 | },
225 | desc = 'desc'
226 | )
227 |
228 | i: 2
229 | test_that({
230 | a
231 | b
232 | }, desc = 'desc')
233 |
234 | i: 3
235 | test_that(
236 | {
237 | a
238 | b
239 | },
240 | desc = 'desc'
241 | )
242 |
243 |
244 | # can reshape with multiple braced expressions
245 |
246 | Code
247 | code <- "foo({\n 1\n}, {\n 2\n})"
248 | snap_reshape_cycle(2, code)
249 | Output
250 | i: 1
251 | foo(
252 | {
253 | 1
254 | },
255 | {
256 | 2
257 | }
258 | )
259 |
260 | i: 2
261 | foo({
262 | 1
263 | }, {
264 | 2
265 | })
266 |
267 |
268 | # String arguments are correctly indented
269 |
270 | Code
271 | code <- "foo({\n 'baz'\n 'foofy'\n})"
272 | snap_reshape_cycle(3, code)
273 | Output
274 | i: 1
275 | foo(
276 | {
277 | 'baz'
278 | 'foofy'
279 | }
280 | )
281 |
282 | i: 2
283 | foo({
284 | 'baz'
285 | 'foofy'
286 | })
287 |
288 | i: 3
289 | foo(
290 | {
291 | 'baz'
292 | 'foofy'
293 | }
294 | )
295 |
296 | Code
297 | code <- "foo('desc', 'bar', {\n 'baz'\n 'foofy'\n})"
298 | snap_reshape_cycle(3, code)
299 | Output
300 | i: 1
301 | foo(
302 | 'desc',
303 | 'bar',
304 | {
305 | 'baz'
306 | 'foofy'
307 | }
308 | )
309 |
310 | i: 2
311 | foo('desc', 'bar', {
312 | 'baz'
313 | 'foofy'
314 | })
315 |
316 | i: 3
317 | foo(
318 | 'desc',
319 | 'bar',
320 | {
321 | 'baz'
322 | 'foofy'
323 | }
324 | )
325 |
326 |
327 | # lines within strings are not indented
328 |
329 | Code
330 | code <- "foo('{\n 1\n 2\n}')"
331 | snap_reshape_cycle(2, code)
332 | Output
333 | i: 1
334 | foo(
335 | '{
336 | 1
337 | 2
338 | }'
339 | )
340 |
341 | i: 2
342 | foo('{
343 | 1
344 | 2
345 | }')
346 |
347 |
348 | # can reshape calls with comments
349 |
350 | Code
351 | code <- "foo(\n x,\n y # comment\n)"
352 | snap_reshape_cycle(2, code)
353 | Output
354 | i: 1
355 | foo(
356 | x,
357 | y # comment
358 | )
359 |
360 | i: 2
361 | foo(
362 | x,
363 | y # comment
364 | )
365 |
366 | Code
367 | code <- "foo(x, y # comment\n)"
368 | snap_reshape_cycle(2, code)
369 | Output
370 | i: 1
371 | foo(
372 | x,
373 | y # comment
374 | )
375 |
376 | i: 2
377 | foo(
378 | x,
379 | y # comment
380 | )
381 |
382 |
383 | # can reshape calls with empty arguments
384 |
385 | Code
386 | code <- "foo(x, , , y, z, )"
387 | snap_reshape_cycle(2, code)
388 | Output
389 | i: 1
390 | foo(
391 | x,
392 | ,
393 | ,
394 | y,
395 | z,
396 |
397 | )
398 |
399 | i: 2
400 | foo(x, , , y, z, )
401 |
402 |
403 |
--------------------------------------------------------------------------------
/tests/testthat/fixtures/calls.R:
--------------------------------------------------------------------------------
1 | #' foobar
2 | foo <- function(bar,
3 | baz) {
4 | quux(1, list(2), 3) # foo
5 | (foo)(4,
6 | 5)
7 | }
8 |
9 |
10 | hop(
11 | hip
12 | )
13 |
--------------------------------------------------------------------------------
/tests/testthat/helper-ast-call.R:
--------------------------------------------------------------------------------
1 | print_longer <- function(text, ..., orig = FALSE) {
2 | call <- sub_call_shape(text)
3 | def <- sub_def_shape(text)
4 |
5 | indent <- regexpr("[^[:space:]]", text) - 1
6 | indent <- strrep(" ", indent)
7 |
8 | cat_line(
9 | if (orig) cat_line(c(call, "")),
10 | paste0(indent, as_longer(call, ...)),
11 | "",
12 | paste0(indent, as_longer(def, ...))
13 | )
14 | }
15 | print_longer_l <- function(text, ...) {
16 | print_longer(text, L = TRUE, ...)
17 | }
18 | as_longer <- function(text, ...) {
19 | info <- parse_info(text = text)
20 | node_call_longer(
21 | parse_xml_one(info),
22 | info = info,
23 | ...
24 | )
25 | }
26 |
27 | print_wider <- function(text, ...) {
28 | call <- sub_call_shape(text)
29 | def <- sub_def_shape(text)
30 |
31 | indent <- regexpr("[^[:space:]]", text) - 1
32 | indent <- strrep(" ", indent)
33 |
34 | cat_line(
35 | paste0(indent, as_wider(call, ...)),
36 | "",
37 | paste0(indent, as_wider(def, ...))
38 | )
39 | }
40 | as_wider <- function(text, ...) {
41 | info <- parse_info(text = text)
42 | node_call_wider(
43 | parse_xml_one(info),
44 | info = info,
45 | ...
46 | )
47 | }
48 |
49 | expect_call_shape <- function(text, type) {
50 | call <- sub_call_shape(text)
51 | expect_equal(
52 | node_call_shape(p(call)),
53 | type
54 | )
55 |
56 | def <- sub_def_shape(text)
57 | expect_equal(
58 | node_call_shape(p(def)),
59 | type
60 | )
61 | }
62 |
63 | sub_call_shape <- function(text) {
64 | sub("\\(", "foofybaz\\(", text)
65 | }
66 | sub_def_shape <- function(text) {
67 | def <- sub("\\(", "function\\(", text)
68 | sub("\\)[[:space:]]*$", "\\) NULL", def)
69 | }
70 |
--------------------------------------------------------------------------------
/tests/testthat/helper-codegrip.R:
--------------------------------------------------------------------------------
1 | p <- function(text) {
2 | parse_xml_one(parse_info(text = text))
3 | }
4 |
--------------------------------------------------------------------------------
/tests/testthat/helper-reshape.R:
--------------------------------------------------------------------------------
1 | snap_reshape_cycle <- function(n, code, line = 1, col = 1) {
2 | for (i in seq_len(n)) {
3 | info <- parse_info(text = code)
4 | out <- reshape_info(line, col, info = info)
5 | code <- if (length(out$reshaped)) out$reshaped else code
6 |
7 | cat_line(
8 | sprintf("i: %d", i),
9 | code,
10 | ""
11 | )
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testthat/test-ast-call.R:
--------------------------------------------------------------------------------
1 | test_that("can find function call node for position", {
2 | path <- test_path("fixtures", "calls.R")
3 | xml <- parse_xml(parse_info(path))
4 | calls <- find_function_calls(xml)
5 | lines <- readLines(path)
6 |
7 | nodes <- function(line) {
8 | cols <- seq_len(nchar(lines[[line]]))
9 | vapply(
10 | cols,
11 | function(col) locate_node(calls, line, col, data = xml),
12 | integer(1)
13 | )
14 | }
15 |
16 | call_nodes <- lapply(seq_along(lines), nodes)
17 | expect_snapshot({
18 | "Node locations of function calls for all combinations of line and col"
19 | call_nodes
20 | })
21 |
22 | node <- find_function_call(4, 4, data = xml)
23 | expect_snapshot({
24 | "Positions of function call at 4:4"
25 | node_positions(node)[1:4]
26 | })
27 | })
28 |
29 | test_that("can retrieve function call text", {
30 | path <- test_path("fixtures", "calls.R")
31 | info <- parse_info(path)
32 | xml <- parse_xml(info)
33 |
34 | expect_snapshot({
35 | "Cursor on `function`"
36 | node <- find_function_call(2, 13, data = xml)
37 | cat_line(node_text(node, info = info))
38 |
39 | "Cursor on `quux`"
40 | node <- find_function_call(4, 4, data = xml)
41 | cat_line(node_text(node, info = info))
42 |
43 | "Cursor on complex call"
44 | node <- find_function_call(5, 3, data = xml)
45 | cat_line(node_text(node, info = info))
46 |
47 | "Cursor on `hop`"
48 | node <- find_function_call(11, 1, data = xml)
49 | cat_line(node_text(node, info = info))
50 | })
51 | })
52 |
53 | test_that("find_function_calls() selects from current node", {
54 | info <- parse_info(text = "foo(bar())")
55 | xml <- parse_xml(info)
56 |
57 | calls <- find_function_calls(xml)
58 | expect_length(calls, 2)
59 |
60 | expect_equal(
61 | node_text(calls[[1]], info = info),
62 | "foo(bar())"
63 | )
64 | expect_equal(
65 | node_text(calls[[2]], info = info),
66 | "bar()"
67 | )
68 |
69 | expect_equal(
70 | find_function_calls(calls[[1]]),
71 | calls
72 | )
73 |
74 | inner_calls <- find_function_calls(calls[[2]])
75 | expect_length(inner_calls, 1)
76 |
77 | expect_equal(
78 | inner_calls[[1]],
79 | calls[[2]]
80 | )
81 | })
82 |
83 | test_that("check_call() detects calls", {
84 | expr <- parse_xml_one(parse_info(text = "foo()"))
85 | expect_true(node_is_call(expr))
86 |
87 | expr <- parse_xml_one(parse_info(text = "foo(bar())"))
88 | expect_true(node_is_call(expr))
89 |
90 | expr <- parse_xml_one(parse_info(text = "foo + bar"))
91 | expect_false(node_is_call(expr))
92 |
93 | fn <- function(x) check_call(x)
94 | expect_snapshot({
95 | (expect_error(fn(expr)))
96 | })
97 | })
98 |
99 | test_that("can retrieve arguments of calls", {
100 | expr <- parse_xml_one(parse_info(text = "foo()"))
101 | expect_equal(
102 | node_call_arguments(expr),
103 | list()
104 | )
105 |
106 | info <- parse_info(text = "foo(1, 2, 3)")
107 | expr <- parse_xml_one(info)
108 | args <- node_call_arguments(expr)
109 |
110 | expect_equal(
111 | node_list_text(args, info = info),
112 | list("1", "2", "3")
113 | )
114 |
115 | info <- parse_info(text = "foo(a = 1, b, c = 3 + \n4)")
116 | node <- parse_xml_one(info)
117 | args <- node_call_arguments(node)
118 |
119 | expect_equal(
120 | node_list_text(args, info = info),
121 | list("a = 1", "b", "c = 3 + \n4")
122 | )
123 | })
124 |
125 | test_that("can retrieve arguments of function definitions", {
126 | expr <- parse_xml_one(parse_info(text = "function() NULL"))
127 | expect_equal(
128 | node_call_arguments(expr),
129 | list()
130 | )
131 |
132 | info <- parse_info(text = "function(a, b, c) NULL")
133 | expr <- parse_xml_one(info)
134 | args <- node_call_arguments(expr)
135 |
136 | expect_equal(
137 | node_list_text(args, info = info),
138 | list("a", "b", "c")
139 | )
140 |
141 | info <- parse_info(text = "function(a = 1, b, c = 3) NULL")
142 | expr <- parse_xml_one(info)
143 | args <- node_call_arguments(expr)
144 |
145 | expect_equal(
146 | node_list_text(args, info = info),
147 | list("a = 1", "b", "c = 3")
148 | )
149 |
150 | info <- parse_info(text = "function(a = 1, b, c = 3 + \n4) NULL")
151 | node <- parse_xml_one(info)
152 | args <- node_call_arguments(node)
153 |
154 | expect_equal(
155 | node_list_text(args, info = info),
156 | list("a = 1", "b", "c = 3 + \n4")
157 | )
158 | })
159 |
160 | test_that("can retrieve argument of if calls", {
161 | info <- parse_info(text = "if (a) b")
162 | node <- parse_xml_one(info)
163 | expect_equal(
164 | map(node_call_arguments(node), node_text, info = info),
165 | list("a")
166 | )
167 |
168 | info <- parse_info(text = "if (a) b else c")
169 | node <- parse_xml_one(info)
170 | expect_equal(
171 | map(node_call_arguments(node), node_text, info = info),
172 | list("a")
173 | )
174 | })
175 |
176 | test_that("can retrieve body of prefix calls", {
177 | expect_null(node_call_body(p("foo()")))
178 |
179 | info <- parse_info(text = "function(a) b")
180 | node <- parse_xml_one(info)
181 | expect_equal(
182 | map(node_call_body(node), node_text, info = info),
183 | list("b")
184 | )
185 |
186 | info <- parse_info(text = "while (a) b")
187 | node <- parse_xml_one(info)
188 | expect_equal(
189 | map(node_call_body(node), node_text, info = info),
190 | list("b")
191 | )
192 |
193 | info <- parse_info(text = "if (a) b")
194 | node <- parse_xml_one(info)
195 | expect_equal(
196 | map(node_call_body(node), node_text, info = info),
197 | list("b")
198 | )
199 |
200 | info <- parse_info(text = "if (a) b else c")
201 | node <- parse_xml_one(info)
202 | expect_equal(
203 | map(node_call_body(node), node_text, info = info),
204 | list("b", "else", "c")
205 | )
206 |
207 | expect_error(
208 | node_call_body(p("for (i in x) b")),
209 | "must be a function call node"
210 | )
211 | })
212 |
213 | test_that("can detect prefix calls", {
214 | expect_equal(
215 | node_call_type(p("function(a) NULL")),
216 | "prefix"
217 | )
218 | expect_equal(
219 | node_call_type(p("if (a) NULL")),
220 | "prefix"
221 | )
222 | expect_equal(
223 | node_call_type(p("while (a) NULL")),
224 | "prefix"
225 | )
226 |
227 | # `for` calls are not ordinary parenthesised expressions
228 | expect_error(
229 | node_call_type(p("for (x in i) NULL")),
230 | "must be a function call node"
231 | )
232 | })
233 |
234 | test_that("node_call_arguments() supports empty arguments", {
235 | node <- p("foo(x = x, , y, z, )")
236 | children <- xml_children(node)
237 | empty <- children[0]
238 |
239 | expect_equal(
240 | node_call_arguments(node),
241 | list(
242 | children[3:5],
243 | empty,
244 | children[8],
245 | children[10],
246 | empty
247 | )
248 | )
249 | })
250 |
--------------------------------------------------------------------------------
/tests/testthat/test-ast.R:
--------------------------------------------------------------------------------
1 | test_that("Can detect indentations", {
2 | info <- parse_info(test_path("fixtures", "calls.R"))
3 | xml <- parse_xml(info)
4 |
5 | calls <- find_function_calls(xml)
6 |
7 | indents <- sapply(calls, function(call) node_indentation(call, info = info))
8 | expect_equal(
9 | indents,
10 | c(0L, 2L, 2L, 2L, 0L)
11 | )
12 | })
13 |
14 | test_that("indent_adjust() works", {
15 | code <- as_lines("{
16 | a
17 | b
18 | }")
19 | exp <- as_lines(" {
20 | a
21 | b
22 | }")
23 | out <- indent_adjust(code, 2)
24 | expect_equal(out, exp)
25 | expect_equal(indent_adjust(out, -2), code)
26 |
27 | # Newlines in strings are preserved
28 | code <- as_lines("{'
29 | a
30 | b
31 | '}")
32 | exp <- as_lines(" {'
33 | a
34 | b
35 | '}")
36 | out <- indent_adjust(code, 2)
37 | expect_equal(out, exp)
38 | expect_equal(indent_adjust(out, -2), code)
39 | })
40 |
--------------------------------------------------------------------------------
/tests/testthat/test-move.R:
--------------------------------------------------------------------------------
1 | test_that("can move outside", {
2 | code <-
3 | "foo({
4 | bar(
5 | point(4, 5)
6 | )
7 | })
8 | "
9 |
10 | info <- parse_info(text = code)
11 |
12 | expect_null(
13 | move_outside_info(1, 3, info = info)
14 | )
15 | expect_equal(
16 | move_outside_info(3, 5, info = info),
17 | c(line = 2, col = 3)
18 | )
19 | expect_equal(
20 | move_outside_info(2, 3, info = info),
21 | c(line = 1, col = 5)
22 | )
23 | expect_equal(
24 | move_outside_info(1, 5, info = info),
25 | c(line = 1, col = 1)
26 | )
27 | expect_null(
28 | move_outside_info(1, 1, info = info)
29 | )
30 | })
31 |
32 | test_that("can move inside and outside", {
33 | code <-
34 | "foo[bar[[
35 | baz({
36 | quux
37 | })
38 | ]]]"
39 | info <- parse_info(text = code)
40 |
41 | a <- c(line = 1, col = 1)
42 | b <- c(line = 1, col = 5)
43 | c <- c(line = 2, col = 3)
44 | d <- c(line = 2, col = 7)
45 | e <- c(line = 3, col = 5)
46 |
47 | expect_equal(inject(move_inside_info(!!!a, info = info)), b)
48 | expect_equal(inject(move_inside_info(!!!b, info = info)), c)
49 | expect_equal(inject(move_inside_info(!!!c, info = info)), d)
50 | expect_equal(inject(move_inside_info(!!!d, info = info)), e)
51 | expect_null(inject(move_inside_info(!!!e, info = info)))
52 |
53 | expect_equal(inject(move_outside_info(!!!e, info = info)), d)
54 | expect_equal(inject(move_outside_info(!!!d, info = info)), c)
55 | expect_equal(inject(move_outside_info(!!!c, info = info)), b)
56 | expect_equal(inject(move_outside_info(!!!b, info = info)), a)
57 | expect_null(inject(move_outside_info(!!!a, info = info)))
58 | })
59 |
60 | test_that("can move inside parens", {
61 | code <- "(a)(b)(c())"
62 | info <- parse_info(text = code)
63 |
64 | out <- map(1:11, function(col) move_inside_info(1, col, info = info)[[2]])
65 | exp <- list(2, NULL, NULL, 5, NULL, NULL, 8, 10, 10, NULL, NULL)
66 | expect_equal(out, exp)
67 | })
68 |
69 | test_that("can move inside prefix fn", {
70 | info <- parse_info(text = "function() {}")
71 | expect_equal(
72 | move_inside_info(1, 1, info = info),
73 | c(line = 1, col = 10)
74 | )
75 | info <- parse_info(text = "if (a) {}")
76 | expect_equal(
77 | move_inside_info(1, 1, info = info),
78 | c(line = 1, col = 5)
79 | )
80 | })
81 |
82 | test_that("can't move inside binary ops", {
83 | info <- parse_info(text = "foo + bar()")
84 | expect_null(move_outside_info(1, 1, info = info))
85 | })
86 |
87 | test_that("can move to next and previous", {
88 | code <-
89 | "foo({
90 | bar(
91 | point(1, 1)
92 | )
93 | })
94 | "
95 | info <- parse_info(text = code)
96 |
97 | a <- c(line = 1, col = 1)
98 | b <- c(line = 1, col = 4)
99 | c <- c(line = 1, col = 5)
100 | d <- c(line = 2, col = 6)
101 | e <- c(line = 3, col = 10)
102 |
103 | expect_equal(inject(move_next_info(!!!a, info = info)), b)
104 | expect_equal(inject(move_next_info(!!!b, info = info)), c)
105 | expect_equal(inject(move_next_info(!!!c, info = info)), d)
106 | expect_equal(inject(move_next_info(!!!d, info = info)), e)
107 | expect_null(inject(move_next_info(!!!e, info = info)))
108 |
109 | expect_equal(inject(move_previous_info(!!!e, info = info)), d)
110 | expect_equal(inject(move_previous_info(!!!d, info = info)), c)
111 | expect_equal(inject(move_previous_info(!!!c, info = info)), b)
112 | expect_null(inject(move_previous_info(!!!b, info = info)))
113 | expect_null(inject(move_previous_info(!!!a, info = info)))
114 | })
115 |
116 | test_that("moving inside with cursor on whitespace", {
117 | info <- parse_info(text = " foo()")
118 | expect_equal(
119 | move_inside_info(1, 1, info = info),
120 | c(line = 1, col = 7)
121 | )
122 |
123 | code <-
124 | "{
125 | foo()
126 | }"
127 | info <- parse_info(text = code)
128 | expect_equal(
129 | move_inside_info(1, 2, info = info),
130 | c(line = 2, col = 7)
131 | )
132 | expect_equal(
133 | move_inside_info(2, 1, info = info),
134 | c(line = 2, col = 7)
135 | )
136 |
137 | code <-
138 | "function() {
139 | bar()
140 | 1 + foo()
141 | }"
142 | info <- parse_info(text = code)
143 | expect_equal(
144 | move_inside_info(3, 1, info = info),
145 | c(line = 3, col = 11)
146 | )
147 | })
148 |
149 | test_that("don't move past closing delims", {
150 | code <-
151 | "{
152 | {
153 | NULL
154 | }
155 | foo()
156 | }"
157 | info <- parse_info(text = code)
158 | expect_null(move_inside_info(3, 1, info = info))
159 | })
160 |
--------------------------------------------------------------------------------
/tests/testthat/test-reshape-call.R:
--------------------------------------------------------------------------------
1 | test_that("can detect call type", {
2 | expect_call_shape("()", "wide")
3 | expect_call_shape("(a)", "wide")
4 | expect_call_shape("(a, b, c)", "wide")
5 | expect_call_shape("\n(a, b, c)\n", "wide")
6 |
7 | # Aligned argument or paren determines L shape
8 | expect_call_shape("(\n )", "L")
9 | expect_call_shape("(a,\n b)", "L")
10 |
11 | # Simple heuristic: first argument determines wide shape
12 | expect_call_shape("(a,\n b, c)", "wide")
13 | expect_call_shape("(a, b, c\n)", "wide")
14 | expect_call_shape("(a, b = b(\n), c)", "wide")
15 |
16 | # Simple heuristic: unaligned argument or paren determines long shape
17 | expect_call_shape("(\n)", "long")
18 | expect_call_shape("(\na)", "long")
19 | expect_call_shape("(\na, b, c)", "long")
20 | expect_call_shape("(\n\na, b, c)", "long")
21 | })
22 |
23 | test_that("can reshape call longer", {
24 | expect_snapshot({
25 | print_longer("()")
26 | print_longer("(a)")
27 | print_longer("(b, c)")
28 | print_longer("(a, b, c)")
29 | print_longer("(a = 1, b, c = 3)")
30 |
31 | "Leading indentation is preserved. First line is not indented"
32 | "because the reshaped text is meant to be inserted at the node"
33 | "coordinates."
34 | print_longer(" ()")
35 | print_longer(" (a)")
36 | print_longer(" (a, b)")
37 |
38 | "Multiline args are indented as is"
39 | print_longer("(a, b = foo(\n bar\n), c)")
40 | print_longer("(a, b =\n 2, c)")
41 | print_longer(" (a, b = foo(\n bar \n ), c)")
42 |
43 | "Wrong indentation is preserved"
44 | print_longer("(a, b = foo(\nbar\n), c)")
45 | print_longer(" (a, b = foo(\n bar\n), c)")
46 | })
47 | })
48 |
49 | test_that("can reshape call longer (L shape)", {
50 | expect_snapshot({
51 | print_longer_l("()")
52 | print_longer_l("(a)")
53 | print_longer_l("(a, b)")
54 | print_longer_l("(a, b, c)")
55 | print_longer_l("(a = 1, b, c = 3)")
56 |
57 | "Leading indentation is preserved. First line is not indented"
58 | "because the reshaped text is meant to be inserted at the node"
59 | "coordinates."
60 | print_longer_l(" ()")
61 | print_longer_l(" (a)")
62 | print_longer_l(" (a, b)")
63 |
64 | "Multiline args are indented as is"
65 | print_longer_l("(a, b = foo(\n bar\n), c)")
66 | print_longer_l("(a, b =\n 2, c)")
67 | print_longer_l(" (a, b = foo(\n bar \n ), c)")
68 |
69 | "Wrong indentation is preserved"
70 | print_longer_l("(a, b = foo(\nbar\n), c)")
71 | print_longer_l(" (a, b = foo(\n bar\n), c)")
72 | })
73 | })
74 |
75 | test_that("can reshape call wider", {
76 | expect_snapshot({
77 | print_wider("()")
78 | print_wider("(\n a\n)")
79 | print_wider("(\n\n a\n\n)")
80 | print_wider("(\n a, \n b\n)")
81 | print_wider("(\n a, \n b, \n c\n)")
82 | print_wider("(\n a = 1,\n b,\n c = 3\n)")
83 |
84 | "Leading indentation is ignored"
85 | print_wider(" ()")
86 | print_wider(" (\n a\n)")
87 | print_wider(" (\n\n a\n\n,\n b)")
88 |
89 | "Multiline args are indented as is"
90 | print_wider("(\n a,\n b = foo(\n bar\n ),\n c)")
91 | print_wider("(\n a,\n b =\n 2,\n c\n)")
92 | })
93 | })
94 |
--------------------------------------------------------------------------------
/tests/testthat/test-reshape.R:
--------------------------------------------------------------------------------
1 | test_that("reshape() cycles function calls and definitions", {
2 | expect_snapshot({
3 | code <- "foofybaz()"
4 | snap_reshape_cycle(2, code)
5 |
6 | code <- "foofybaz(a)"
7 | snap_reshape_cycle(3, code)
8 |
9 | code <- "foofybaz(a, b = 1, c)"
10 | snap_reshape_cycle(3, code)
11 |
12 |
13 | code <- "function() NULL"
14 | snap_reshape_cycle(2, code)
15 |
16 | code <- "function(a) NULL"
17 | snap_reshape_cycle(3, code)
18 |
19 | code <- "function(a, b = 1, c) NULL"
20 | snap_reshape_cycle(4, code)
21 | })
22 | })
23 |
24 | # Might change in the future
25 | test_that("reshape() cycles other call-like constructs", {
26 | expect_snapshot({
27 | code <- "if (a) NULL"
28 | snap_reshape_cycle(2, code)
29 |
30 | code <- "if (a) b else c"
31 | snap_reshape_cycle(2, code)
32 |
33 | code <- "while (a) NULL"
34 | snap_reshape_cycle(2, code)
35 |
36 | code <- "for (i in x) NULL"
37 | snap_reshape_cycle(1, code)
38 | })
39 | })
40 |
41 | test_that("can reshape braced expressions", {
42 | expect_snapshot({
43 | code <-
44 | "expect_snapshot({
45 | a
46 | b
47 | })"
48 | snap_reshape_cycle(2, code)
49 |
50 | code <-
51 | "{
52 | expect_snapshot({
53 | a
54 | b
55 | })
56 | }"
57 | snap_reshape_cycle(2, code, line = 2, col = 3)
58 |
59 | code <-
60 | "test_that('desc', {
61 | a
62 | b
63 | })"
64 | snap_reshape_cycle(3, code)
65 |
66 | code <-
67 | "test_that({
68 | a
69 | b
70 | }, desc = 'desc')"
71 | snap_reshape_cycle(3, code)
72 | })
73 | })
74 |
75 | test_that("can reshape with multiple braced expressions", {
76 | expect_snapshot({
77 | code <- "foo({
78 | 1
79 | }, {
80 | 2
81 | })"
82 | snap_reshape_cycle(2, code)
83 | })
84 | })
85 |
86 | test_that("empty lines are not indented when reshaped", {
87 | code <-
88 | "foo({
89 | 1
90 |
91 | 2
92 | })"
93 |
94 | exp <-
95 | "foo(
96 | {
97 | 1
98 |
99 | 2
100 | }
101 | )"
102 |
103 | expect_equal(
104 | reshape(1, 2, info = parse_info(text = code)),
105 | exp
106 | )
107 | })
108 |
109 | test_that("String arguments are correctly indented", {
110 | expect_snapshot({
111 | code <- "foo({\n 'baz'\n 'foofy'\n})"
112 | snap_reshape_cycle(3, code)
113 |
114 | code <- "foo('desc', 'bar', {\n 'baz'\n 'foofy'\n})"
115 | snap_reshape_cycle(3, code)
116 | })
117 | })
118 |
119 | test_that("lines within strings are not indented", {
120 | expect_snapshot({
121 | code <-
122 | "foo('{
123 | 1
124 | 2
125 | }')"
126 | snap_reshape_cycle(2, code)
127 | })
128 | })
129 |
130 | test_that("can reshape calls with comments", {
131 | expect_snapshot({
132 | code <-
133 | "foo(
134 | x,
135 | y # comment
136 | )"
137 | snap_reshape_cycle(2, code)
138 |
139 | code <-
140 | "foo(x, y # comment
141 | )"
142 | snap_reshape_cycle(2, code)
143 | })
144 | })
145 |
146 | test_that("can reshape calls with empty arguments", {
147 | expect_snapshot({
148 | code <- "foo(x, , , y, z, )"
149 | snap_reshape_cycle(2, code)
150 | })
151 | })
152 |
--------------------------------------------------------------------------------
/tests/testthat/test-text.R:
--------------------------------------------------------------------------------
1 | test_that("can skip whitespace", {
2 | lines <- "foo"
3 | expect_equal(
4 | skip_space(lines, 1, 1),
5 | c(line = 1, col = 1)
6 | )
7 | expect_equal(
8 | skip_space(lines, 1, 3),
9 | c(line = 1, col = 3)
10 | )
11 |
12 | lines <- " foo bar"
13 | expect_equal(
14 | skip_space(lines, 1, 1),
15 | c(line = 1, col = 3)
16 | )
17 | expect_equal(
18 | skip_space(lines, 1, 6),
19 | c(line = 1, col = 8)
20 | )
21 | expect_equal(
22 | skip_space(lines, 1, 8),
23 | c(line = 1, col = 8)
24 | )
25 |
26 | lines <- c("foo", " bar")
27 | expect_equal(
28 | skip_space(lines, 2, 1),
29 | c(line = 2, col = 3)
30 | )
31 | expect_equal(
32 | skip_space(lines, 2, 3),
33 | c(line = 2, col = 3)
34 | )
35 |
36 | lines <- c("foo ", "", "bar")
37 | expect_equal(
38 | skip_space(lines, 1, 4),
39 | c(line = 3, col = 1)
40 | )
41 | expect_equal(
42 | skip_space(lines, 2, 1),
43 | c(line = 3, col = 1)
44 | )
45 |
46 | # NOTE: Is this correct?
47 | lines <- ""
48 | expect_equal(
49 | skip_space(lines, 1, 1),
50 | c(line = 1, col = 0)
51 | )
52 |
53 | lines <- " "
54 | expect_equal(
55 | skip_space(lines, 1, 1),
56 | c(line = 1, col = 2)
57 | )
58 | expect_equal(
59 | skip_space(lines, 1, 2),
60 | c(line = 1, col = 2)
61 | )
62 |
63 | # NOTE: Should this be `col = 2`?
64 | lines <- c(" ", " ")
65 | expect_equal(
66 | skip_space(lines, 1, 1),
67 | c(line = 2, col = 1)
68 | )
69 | })
70 |
--------------------------------------------------------------------------------