Skip to content

Commit c980131

Browse files
author
Greg Hendershott
committed
Add racket-align and racket-unalign
1 parent 3265c8b commit c980131

File tree

4 files changed

+197
-4
lines changed

4 files changed

+197
-4
lines changed

Reference.md

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
## Run
2020

2121
### racket-run
22-
<kbd>C-c C-k</kbd>
22+
<kbd>C-c C-k</kbd> or <kbd>C-c C-c</kbd>
2323

2424
Save and evaluate the buffer in REPL, much like DrRacket's Run.
2525

@@ -540,6 +540,74 @@ If you don’t like the highlighting of partially matching tokens you
540540
can turn it off by setting `input-method-highlight-flag` to nil via
541541
`M-x customize-variable`.
542542

543+
### racket-align
544+
<kbd>M-[</kbd>
545+
546+
Align values in the same column.
547+
548+
Useful for binding forms like `let` and `parameterize`,
549+
conditionals like `cond` and `match`, association lists, and any
550+
series of couples like the arguments to `hash`.
551+
552+
Before choosing this command, put point on the first of a series
553+
of "couples". A couple is:
554+
555+
- A list of two or more sexprs: `[sexpr val sexpr ...]`
556+
- Two sexprs: `sexpr val`.
557+
558+
Each `val` moves to the same column and is `indent-sexp`-ed (in
559+
case it is a multi-line form).
560+
561+
For example with point on the `[` before `a`:
562+
563+
Before After
564+
565+
(let ([a 12] (let ([a 12]
566+
[bar 23]) [bar 23])
567+
....) ....)
568+
569+
'([a . 12] '([a . 12]
570+
[bar . 23]) [bar . 23])
571+
572+
(cond [a? #t] (cond [a? #t]
573+
[b? (f x [b? (f x
574+
y)] y)]
575+
[else #f]) [else #f])
576+
577+
Or with point on the `'` before `a`:
578+
579+
(list 'a 12 (list 'a 12
580+
'bar 23) 'bar 23)
581+
582+
If more than one couple is on the same line, none are aligned,
583+
because it is unclear where the value column should be. For
584+
example the following form will not change; [`racket-align`](#racket-align) will
585+
display an error message:
586+
587+
(let ([a 0][b 1]
588+
[c 2]) error; unchanged
589+
....)
590+
591+
When a couple's sexprs start on different lines, that couple is
592+
ignored. Other, single-line couples in the series are aligned as
593+
usual. For example:
594+
595+
(let ([foo (let ([foo
596+
0] 0]
597+
[bar 1] [bar 1]
598+
[x 2]) [x 2])
599+
....) ....)
600+
601+
See also: [`racket-unalign`](#racket-unalign).
602+
603+
### racket-unalign
604+
<kbd>M-{</kbd>
605+
606+
The opposite of [`racket-align`](#racket-align).
607+
608+
Effectively does M-x `just-one-space` and `indent-sexp` for each
609+
couple's value.
610+
543611
## Macro expand
544612

545613
### racket-expand-region

racket-edit.el

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,125 @@ special commands to navigate among the definition and its uses.
873873
(racket--unhighlight-all)
874874
(setq buffer-read-only nil)))
875875

876+
877+
;;; align
878+
879+
(defun racket-align ()
880+
"Align values in the same column.
881+
882+
Useful for binding forms like `let` and `parameterize`,
883+
conditionals like `cond` and `match`, association lists, and any
884+
series of couples like the arguments to `hash`.
885+
886+
Before choosing this command, put point on the first of a series
887+
of \"couples\". A couple is:
888+
889+
- A list of two or more sexprs: `[sexpr val sexpr ...]`
890+
- Two sexprs: `sexpr val`.
891+
892+
Each `val` moves to the same column and is `indent-sexp'-ed (in
893+
case it is a multi-line form).
894+
895+
For example with point on the `[` before `a`:
896+
897+
Before After
898+
899+
(let ([a 12] (let ([a 12]
900+
[bar 23]) [bar 23])
901+
....) ....)
902+
903+
'([a . 12] '([a . 12]
904+
[bar . 23]) [bar . 23])
905+
906+
(cond [a? #t] (cond [a? #t]
907+
[b? (f x [b? (f x
908+
y)] y)]
909+
[else #f]) [else #f])
910+
911+
Or with point on the `'` before `a`:
912+
913+
(list 'a 12 (list 'a 12
914+
'bar 23) 'bar 23)
915+
916+
If more than one couple is on the same line, none are aligned,
917+
because it is unclear where the value column should be. For
918+
example the following form will not change; `racket-align' will
919+
display an error message:
920+
921+
(let ([a 0][b 1]
922+
[c 2]) error; unchanged
923+
....)
924+
925+
When a couple's sexprs start on different lines, that couple is
926+
ignored. Other, single-line couples in the series are aligned as
927+
usual. For example:
928+
929+
(let ([foo (let ([foo
930+
0] 0]
931+
[bar 1] [bar 1]
932+
[x 2]) [x 2])
933+
....) ....)
934+
935+
See also: `racket-unalign'."
936+
(interactive)
937+
(save-excursion
938+
(let ((listp (eq ?\( (char-syntax (char-after))))
939+
(prev-line 0)
940+
(max-col 0))
941+
(racket--for-each-couple listp
942+
(lambda ()
943+
(setq max-col (max max-col (current-column)))
944+
(let ((this-line (line-number-at-pos)))
945+
(when (= prev-line this-line)
946+
(user-error
947+
"Can't align if any couples are on same line"))
948+
(setq prev-line this-line))))
949+
(racket--for-each-couple listp
950+
(lambda ()
951+
(indent-to max-col)
952+
(indent-sexp))))))
953+
954+
(defun racket-unalign ()
955+
"The opposite of `racket-align'.
956+
957+
Effectively does M-x `just-one-space' and `indent-sexp' for each
958+
couple's value."
959+
(interactive)
960+
(save-excursion
961+
(let ((listp (eq ?\( (char-syntax (char-after)))))
962+
(racket--for-each-couple listp
963+
(lambda ()
964+
(just-one-space)
965+
(indent-sexp))))))
966+
967+
(defun racket--for-each-couple (listp f)
968+
"Move point to each value sexp of a couple, and `funcall' F.
969+
970+
Only call F when the couple's sexprs are on the same line.
971+
972+
When LISTP is true, expects couples to be `[id val]`, else `id val`."
973+
(save-excursion
974+
(condition-case ()
975+
(while t
976+
(when listp
977+
(down-list))
978+
(forward-sexp)
979+
(let ((line (line-number-at-pos)))
980+
(forward-sexp)
981+
(backward-sexp)
982+
(when (= line (line-number-at-pos))
983+
;; Defensive: Backup over any prefix or punctuation
984+
;; chars just in case backward-sexp didn't (although it
985+
;; should have if our syntax table is correct).
986+
(while (memq (char-syntax (char-before)) '(?\' ?\.))
987+
(goto-char (1- (point))))
988+
(funcall f)))
989+
;; On to the next couple...
990+
(if listp
991+
(up-list)
992+
(forward-sexp)))
993+
(scan-error nil))))
994+
876995

877996
;;; misc
878997

racket-make-doc.el

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282
racket-backward-up-list
8383
racket-check-syntax-mode
8484
racket-unicode-input-method-enable
85+
racket-align
86+
racket-unalign
8587
"Macro expand"
8688
racket-expand-region
8789
racket-expand-definition

racket-mode.el

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ http://www.gnu.org/licenses/ for details.")
5555

5656
(defvar racket-mode-map
5757
(racket--easy-keymap-define
58-
'((("C-c C-k"
59-
"C-c C-c") racket-run)
58+
'((("C-c C-c"
59+
"C-c C-k") racket-run)
6060
("C-c C-z" racket-repl)
6161
("<f5>" racket-run-and-switch-to-repl)
6262
("M-C-<f5>" racket-racket)
@@ -86,7 +86,9 @@ http://www.gnu.org/licenses/ for details.")
8686
("M-C-." racket-visit-module)
8787
("M-," racket-unvisit)
8888
("C-c C-f" racket-fold-all-tests)
89-
("C-c C-u" racket-unfold-all-tests)))
89+
("C-c C-u" racket-unfold-all-tests)
90+
("M-[" racket-align)
91+
("M-{" racket-unalign)))
9092
"Keymap for Racket mode. Inherits from `lisp-mode-shared-map'.")
9193

9294
(easy-menu-define racket-mode-menu racket-mode-map
@@ -118,6 +120,8 @@ http://www.gnu.org/licenses/ for details.")
118120
["Insert λ" racket-insert-lambda]
119121
["Indent Region" indent-region]
120122
["Cycle Paren Shapes" racket-cycle-paren-shapes]
123+
["Align" racket-align]
124+
["Unalign" racket-unalign]
121125
"---"
122126
["Visit Definition" racket-visit-definition]
123127
["Visit Module" racket-visit-module]

0 commit comments

Comments
 (0)