1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
;;; racket-parens.el -*- lexical-binding: t; -*-
;; Copyright (c) 2013-2024 by Greg Hendershott.
;; Portions Copyright (C) 1985-1986, 1999-2013 Free Software Foundation, Inc.
;; Author: Greg Hendershott
;; URL: https://github.com/greghendershott/racket-mode
;; SPDX-License-Identifier: GPL-3.0-or-later
;; Things related to parens, paredit, electric-pair-mode
(require 'racket-custom)
(require 'racket-ppss)
(require 'racket-util)
;;; racket--self-insert
(defun racket--self-insert (event)
"Simulate a `self-insert-command' of EVENT.
Using this intead of `insert' allows self-insert hooks to run,
which is important for things like `electric-pair-mode'.
A command using this should probably set its delete-selection
property to t so that `delete-selection-mode' works:
(put \\='racket-command \\='delete-selection t)
If necessary the value of the property can be a function, for
example `racket--electric-pair-mode-not-active'."
(let ((last-command-event event)) ;set this for hooks
(self-insert-command (prefix-numeric-value nil))))
(defun racket--electric-pair-mode-not-active ()
"A suitable value for the delete-selection property of commands
that insert parens: Inserted text should replace the selection
unless a mode like `electric-pair-mode' is enabled, in which case
the selection is to be wrapped in parens."
(not (and (boundp 'electric-pair-mode)
electric-pair-mode)))
;;; Automatically insert matching \?) \?] or \?}
(defconst racket--matching-parens
'(( ?\( . ?\) )
( ?\[ . ?\] )
( ?\{ . ?\} )))
(defun racket-insert-closing (&optional prefix)
"Insert a matching closing delimiter.
With \\[universal-argument] insert the typed character as-is.
This is handy if you're not yet using something like
`paredit-mode', `smartparens-mode', `parinfer-mode', or simply
`electric-pair-mode' added in Emacs 24.5."
(interactive "P")
(let* ((do-it (not (or prefix
(and (string= "#\\"
(buffer-substring-no-properties
(- (point) 2) (point) )))
(racket--ppss-string-p (syntax-ppss)))))
(open-char (and do-it (racket--open-paren #'backward-up-list)))
(close-pair (and open-char (assq open-char racket--matching-parens)))
(close-char (and close-pair (cdr close-pair))))
(racket--self-insert (or close-char last-command-event))))
(put 'racket-insert-closing 'delete-selection
#'racket--electric-pair-mode-not-active)
(defun racket--open-paren (back-func)
"Use BACK-FUNC to find an opening ( [ or { if any.
BACK-FUNC should be something like #\\='backward-sexp or #\\='backward-up-list."
(save-excursion
(ignore-errors
(funcall back-func)
(let ((ch (char-after)))
(and (eq ?\( (char-syntax ch))
ch)))))
;;; paredit spaces in reader literals and at-expressions
(defun racket--paredit-space-for-delimiter-predicate (endp delimiter)
"A value for hook `paredit-space-for-delimiter-predicates'."
(if (and (racket--mode-edits-racket-p)
(not endp))
(not
(or
;; reader literal: e.g. #(), #hasheq(), #"bstr", #px".*"
(looking-back (rx ?# (* (or (syntax word)
(syntax symbol)
(syntax punctuation))))
nil)
;; at-expression: @foo[ @foo{
(and (memq delimiter '(?\[ ?\{))
(looking-back (rx ?@ (* (or (syntax word)
(syntax symbol)
(syntax punctuation))))
nil))
;; at-expression: @foo[]{
(and (eq delimiter ?\{)
(looking-back (rx ?@ (* (or (syntax word)
(syntax symbol)
(syntax punctuation)))
?\[
(* (or (syntax word)
(syntax symbol)
(syntax punctuation)))
?\])
nil))
))
t))
;;; Cycle paren shapes
(defconst racket--paren-shapes
'( (?\( ?\[ ?\] )
(?\[ ?\{ ?\} )
(?\{ ?\( ?\) ))
"This is not user-configurable because we expect them have to
have actual ?\( and ?\) char syntax.")
(defun racket-cycle-paren-shapes ()
"Cycle the sexpr among () [] {}."
(interactive)
(racket--assert-sexp-edit-mode)
(save-excursion
(unless (eq ?\( (char-syntax (char-after)))
(backward-up-list))
(pcase (assq (char-after) racket--paren-shapes)
(`(,_ ,open ,close)
(delete-char 1)
(insert open)
(backward-char 1)
(forward-sexp 1)
(delete-char -1)
(insert close))
(_
(user-error "Don't know that paren shape")))))
(provide 'racket-parens)
;; racket-parens.el ends here
|