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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
;;; racket-smart-open.el -*- lexical-binding: t; -*-
;; Copyright (c) 2013-2020 by Greg Hendershott.
;; Portions Copyright (C) 1985-1986, 1999-2013 Free Software Foundation, Inc.
;; Author: Greg Hendershott
;; URL: https://github.com/greghendershott/racket-mode
;; License:
;; This 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 2, or (at your option)
;; any later version. This 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. See
;; http://www.gnu.org/licenses/ for details.
;;; racket-smart-open-bracket-mode
(require 'racket-custom)
(require 'racket-parens)
(require 'racket-ppss)
(require 'racket-util)
;;;###autoload
(define-minor-mode racket-smart-open-bracket-mode
"Minor mode to let you always type `[`' to insert `(` or `[` automatically.
Behaves like the \"Automatically adjust opening square brackets\"
feature in Dr. Racket.
By default, inserts a `(`. Inserts a `[` in the following cases:
- `let`-like bindings -- forms with `let` in the name as well
as things like `parameterize`, `with-handlers`, and
`with-syntax`.
- `case`, `cond`, `match`, `syntax-case`, `syntax-parse`, and
`syntax-rules` clauses.
- `for`-like bindings and `for/fold` accumulators.
- `class` declaration syntax, such as `init` and `inherit`.
When the previous s-expression in a sequence is a compound
expression, uses the same kind of delimiter.
To force insert `[`, use `quoted-insert'.
Combined with `racket-insert-closing' this means that you can
press the unshifted `[` and `]` keys to get whatever delimiters
follow the Racket conventions for these forms. When something
like `electric-pair-mode' or `paredit-mode' is active, you need
not even press `]`.
Tip: When also using `paredit-mode', enable that first so that
the binding for the `[`' key in the map for
`racket-smart-open-bracket-mode' has higher priority. See also
the variable `minor-mode-map-alist'."
:lighter " RacketSmartOpen"
:keymap (racket--easy-keymap-define
'(("[" racket-smart-open-bracket)))
(unless (memq major-mode '(racket-mode racket-repl-mode))
(setq racket-smart-open-bracket-mode nil)
(user-error "racket-smart-open-bracket-mode only works with with Racket Mode buffers")))
(defconst racket--smart-open-bracket-data
(eval-when-compile
`(;; cond-like
(0 0 ,(rx (seq "("
(or "augment"
"augment-final"
"augride"
"cond"
"field"
"inherit"
"inherit-field"
"inherit/super"
"inherit/inner"
"init"
"init-field"
"match-lambda"
"match-lambda*"
"match-lambda**"
"overment"
"override"
"override-final"
"public"
"pubment"
"public-final"
"rename-inner"
"rename-super"
"super-new")
(or space line-end))))
;; case-like
(2 0 ,(rx (seq "("
(or "case"
"new"
"match"
"match*"
"syntax-parse"
"syntax-rules")
(or space line-end))))
;; syntax-case
(3 0 ,(rx (seq "("
(or "syntax-case")
(or space line-end))))
;; syntax-case*
(4 0 ,(rx (seq "("
(or "syntax-case*")
(or space line-end))))
;; let-like
;;
;; In addition to the obvious suspects with 'let' in the name,
;; handles forms like 'parameterize', 'with-handlers', 'for',
;; and 'for/fold' accumulator bindings.
(0 1 ,(rx (seq (or "for"
"for/list"
"for/vector"
"for/hash"
"for/hasheq"
"for/hasheqv"
"for/and"
"for/or"
"for/lists"
"for/first"
"for/last"
"for/fold"
"for/flvector"
"for/extflvector"
"for/set"
"for/sum"
"for/product"
"for*"
"for*/list"
"for*/vector"
"for*/hash"
"for*/hasheq"
"for*/hasheqv"
"for*/and"
"for*/or"
"for*/lists"
"for*/first"
"for*/last"
"for*/fold"
"for*/flvector"
"for*/extflvector"
"for*/set"
"for*/sum"
"for*/product"
"fluid-let"
"let"
"let*"
"let*-values"
"let-struct"
"let-syntax"
"let-syntaxes"
"let-values"
"let/cc"
"let/ec"
"letrec"
"letrec-syntax"
"letrec-syntaxes"
"letrec-syntaxes+values"
"letrec-values"
"match-let"
"match-let*"
"match-let-values"
"match-let*-values"
"match-letrec"
"parameterize"
"parameterize*"
"with-handlers"
"with-handlers*"
"with-syntax"
"with-syntax*")
(or space line-end))))
;; for/fold bindings
;;
;; Note: Previous item handles the first, accumulators subform.
(0 2 ,(rx (seq (or "for/fold"
"for*/fold")
(or space line-end))))
;; named-let bindings
;;
(0 2 ,(rx (seq "let" (1+ whitespace) (1+ (not (in "()[]{}\",'`;#|\" "))))))))
"A list of lists. Each sub list is arguments to supply to
`racket--smart-open-bracket-helper'.")
(defun racket--smart-open-bracket-helper (pre-backward-sexps
post-backward-sexps
regexp)
"Is point at a subform of a known form REGEXP that should open with '['.
Returns '[' or nil."
(and (save-excursion
(ignore-errors
(backward-sexp pre-backward-sexps) t))
(save-excursion
(ignore-errors
(backward-up-list)
(backward-sexp post-backward-sexps)
(when (looking-at-p regexp)
?\[)))))
(defun racket-smart-open-bracket (&optional prefix)
"Automatically insert a `(` or a `[` as appropriate.
See `racket-smart-open-bracket-mode'."
(interactive "P")
(let ((ch (or (and (save-excursion
(let ((pt (point)))
(beginning-of-defun)
(let ((state (parse-partial-sexp (point) pt)))
(or (racket--ppss-string-p state)
(racket--ppss-comment-p state)))))
?\[)
(cl-some (lambda (xs)
(apply #'racket--smart-open-bracket-helper xs))
racket--smart-open-bracket-data)
(racket--open-paren #'backward-sexp)
?\()))
(if (fboundp 'racket--paredit-aware-open)
(racket--paredit-aware-open prefix ch)
(racket--self-insert ch))))
(put 'racket-smart-open-bracket 'delete-selection
#'racket--electric-pair-mode-not-active)
(eval-after-load 'paredit
'(progn
(declare-function paredit-open-round 'paredit)
(declare-function paredit-open-square 'paredit)
(declare-function paredit-open-curly 'paredit)
(defun racket--paredit-aware-open (prefix ch)
"A paredit-aware helper for `racket-smart-open-bracket'.
When `paredit-mode' is active, use its functions, such as
`paredit-open-round'. Note: This function isn't defined unless
paredit is loaded, so check for this function's existence using
`fboundp'."
(let ((paredit-active (and (boundp 'paredit-mode) paredit-mode)))
(cond ((not paredit-active) (racket--self-insert ch))
((eq ch ?\() (paredit-open-round prefix))
((eq ch ?\[) (paredit-open-square prefix))
((eq ch ?\{) (paredit-open-curly prefix))
(t (racket--self-insert ch)))))))
(provide 'racket-smart-open)
;; racket-smart-open.el ends her
|