emacs.d/elpa/racket-mode-20200218.1623/racket-parens.el

167 lines
5.7 KiB
EmacsLisp
Raw Normal View History

2019-11-23 09:10:03 +01:00
;;; racket-parens.el -*- lexical-binding: t; -*-
;; Copyright (c) 2013-2019 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.
;; 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 a prefix, 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)
;;; paredit and reader literals
(defun racket--reader-literal-paredit-space-for-delimiter-predicate (endp _delimiter)
"`paredit-mode' shouldn't insert space beteween # and open delimiters.
Examples: #() #2() #fl() #hasheq etc.
This function is a suitable element for the list variable
`paredit-space-for-delimiter-predicates'."
(if (and (racket--mode-edits-racket-p)
(not endp))
(not (looking-back (rx ?# (* (or (syntax word) (syntax symbol))))
nil))
t))
(eval-after-load 'paredit
'(add-hook 'paredit-space-for-delimiter-predicates
#'racket--reader-literal-paredit-space-for-delimiter-predicate))
;;; paredit and at-expressions
(defun racket--at-expression-paredit-space-for-delimiter-predicate (endp delimiter)
"`paredit-mode' shouldn't insert space before [ or { in Racket at-expressions.
This function is a suitable element for the list variable
`paredit-space-for-delimiter-predicates'."
(if (and (racket--mode-edits-racket-p)
(not endp))
(not (or
;; @foo[ @foo{
(and (memq delimiter '(?\[ ?\{))
(looking-back (rx ?@ (* (or (syntax word) (syntax symbol))))
nil))
;; @foo[]{
(and (eq delimiter ?\{)
(looking-back (rx ?@ (* (or (syntax word) (syntax symbol)))
?\[
(* (or (syntax word) (syntax symbol)))
?\])
nil))))
t))
(eval-after-load 'paredit
'(add-hook 'paredit-space-for-delimiter-predicates
#'racket--at-expression-paredit-space-for-delimiter-predicate))
;;; 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)
(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)
(backward-delete-char 1)
(insert close))
(_
(user-error "Don't know that paren shape")))))
(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)))))
(provide 'racket-parens)
;; racket-parens.el ends here