emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/racket-mode 310f195221 3/3: racket-hash-lang-pairs: Handle


From: ELPA Syncer
Subject: [nongnu] elpa/racket-mode 310f195221 3/3: racket-hash-lang-pairs: Handle multi char delimiters
Date: Wed, 6 Dec 2023 19:00:37 -0500 (EST)

branch: elpa/racket-mode
commit 310f1952213b0e4a8cee7e8cd1f057442e9feb0f
Author: Greg Hendershott <git@greghendershott.com>
Commit: Greg Hendershott <git@greghendershott.com>

    racket-hash-lang-pairs: Handle multi char delimiters
---
 doc/racket-mode.texi |  18 ++------
 racket-hash-lang.el  | 121 +++++++++++++++++++++++++++++++--------------------
 2 files changed, 79 insertions(+), 60 deletions(-)

diff --git a/doc/racket-mode.texi b/doc/racket-mode.texi
index b4d8efa2a6..2b0bce483b 100644
--- a/doc/racket-mode.texi
+++ b/doc/racket-mode.texi
@@ -3191,24 +3191,14 @@ Scribble text, use the face @code{default}
 @node racket-hash-lang-pairs
 @subsection racket-hash-lang-pairs
 
-Pairs of characters to insert automatically.
+Pairs of delimiters to insert or delete automatically.
 
-The format of each item is
-
-(open-char close-char . except-kinds)
-
-where except-kinds are symbols corresonding to lang lexer token
-kinds.
+The format of each item is (cons string string).
 
 This is initialized whenever a module language changes, using
-single-character values from the language's reported
+values from the language's reported values for
 drracket:paren-matches and drracket:quote-matches.
 
-Paren matches are allowed for all token kinds. Quote matches are
-not allowed in string, comment, or text tokens. For example a
-lang might supply ' as a quote-match, and you wouldn't want to
-type don't in prose but get don't'.
-
 You may customize this default initialization in
 @ref{racket-hash-lang-module-language-hook}.
 
@@ -3266,7 +3256,7 @@ delimiter-matching modes is likely to work well unless the
 hash-lang uses racket for drracket:grouping-position, in which
 case @ref{racket-hash-lang-mode} uses the classic @ref{racket-mode}
 syntax-table for the buffer. Otherwise you should not enable one
-of these modes, and isntead just use the simple delimiter
+of these modes, and instead just use the simple delimiter
 matching built into @ref{racket-hash-lang-mode}; see
 @ref{racket-hash-lang-pairs}.
 
diff --git a/racket-hash-lang.el b/racket-hash-lang.el
index 0e08a49654..eb5597bbe3 100644
--- a/racket-hash-lang.el
+++ b/racket-hash-lang.el
@@ -9,6 +9,7 @@
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 
 (require 'cl-lib)
+(require 'elec-pair)
 (require 'seq)
 (require 'racket-cmd)
 (require 'racket-mode)
@@ -251,6 +252,8 @@ can contribute more colors; see the customization variable
   (add-hook 'post-self-insert-hook #'racket-hash-lang-post-self-insert nil t)
   (add-hook 'self-insert-uses-region-functions 
#'racket-hash-lang-will-use-region nil t)
   (electric-pair-local-mode -1)
+  (setq-local electric-pair-pairs nil)
+  (setq-local electric-pair-text-pairs nil)
   (setq-local electric-pair-open-newline-between-pairs nil) ;#685
   (electric-indent-local-mode -1)
   (setq-local electric-indent-inhibit t)
@@ -679,18 +682,24 @@ However other users don't need that, so we supply this
 
 ;;; Pairs
 
-;; Because I couldn't see how to make electric-pair-mode work
-;; consistently -- including having it _not_ pair things like ' inside
-;; tokens like comments, strings, text -- I resorted to making a very
-;; simple alternative. Not electric pairs, merely steam-powered.
+;; Although this may seem like (and in fact be) an Alan Perlis
+;; implementation of half of fancier auto-pair modes, we have two
+;; justifications:
+;;
+;; 1. A Racket lang may supply multi-chararacter open and close
+;; delimiters. AFAICT electric-pair-mode can't handle this.
+;;
+;; 2. Even with single characters, I couldn't see how to make
+;; electric-pair-mode work consistently -- including having it _not_
+;; pair things like ' inside tokens like comments, strings, text.
 
 (defvar-local racket-hash-lang-pairs nil
-  "Pairs of characters to insert automatically.
+  "Pairs of delimiters to insert or delete automatically.
 
-The format of each item is (cons open-char close-char).
+The format of each item is (cons string string).
 
 This is initialized whenever a module language changes, using
-single-character values from the language's reported
+values from the language's reported values for
 drracket:paren-matches and drracket:quote-matches.
 
 You may customize this default initialization in
@@ -698,9 +707,9 @@ You may customize this default initialization in
 
 (defvar-local racket-hash-lang-pairs-predicate
   #'racket-hash-lang-pairs-predicate-default)
-(defun racket-hash-lang-pairs-predicate-default (char pos)
+(defun racket-hash-lang-pairs-predicate-default (pair pos)
   (not
-   (and (eq char ?')
+   (and (equal (car pair) "'")
         (pcase-let ((`(,_beg ,_end (,kind . ,_))
                      (racket-hash-lang-classify (1- pos))))
           (memq kind '(string comment text))))))
@@ -715,59 +724,79 @@ You may customize this default initialization in
 
 (defun racket--hash-lang-configure-pairs (paren-matches quote-matches)
   (let ((pairs nil))
-    (cl-flet ((add (open close)
-                   (when (and (= 1 (length open)) (= 1 (length close)))
-                     (push (cons (aref open 0) (aref close 0))
-                           pairs))))
-      (dolist (p paren-matches) (add (car p) (cdr p)))
-      (dolist (q quote-matches) (add q q)))
+    (dolist (p paren-matches) (push p pairs))
+    (dolist (q quote-matches) (push (cons q q) pairs))
     (setq-local racket-hash-lang-pairs (reverse pairs))))
 
-(defun racket--hash-lang-lookup-pair ()
-  (when-let (pair (assq last-command-event racket-hash-lang-pairs))
-    (when (funcall racket-hash-lang-pairs-predicate (car pair) (point))
-      pair)))
+(defun racket--hash-lang-lookup-pair (char pos &optional prefer-larger-match-p)
+  ;; The idea behind PREFER-LARGER-MATCHES-P is that a lang might have
+  ;; paren-matches like both () and '()' as indeed does rhombus. When
+  ;; inserting let's treat that as '' then (). But when deleting back
+  ;; over '( we'd prefer to just delete that as one thing. So here we can
+  ;; lookup either way.
+  ;;
+  ;; This is written _not_ to assume that CHAR is already in the
+  ;; buffer, so that we can be used by a self-insert-uses-region
+  ;; function. Of course when OPEN consists of multiple characters, we
+  ;; must look for the others already in the buffer before POS.
+  (seq-reduce
+   (lambda (answer-so-far pair)
+     (let* ((open (car pair))
+            (len (length open)))
+       (or (and (< 0 (- pos 1 (1- len)))
+                (equal open
+                       (concat
+                        (buffer-substring-no-properties (- pos 1 (1- len)) (- 
pos 1))
+                        (string char)))
+                (funcall racket-hash-lang-pairs-predicate pair (point))
+                (or (not answer-so-far)
+                    (funcall (if prefer-larger-match-p #'> #'<)
+                             (length open) (length (car answer-so-far))))
+                pair)
+           answer-so-far)))
+   racket-hash-lang-pairs
+   nil))
 
 (defun racket-hash-lang-will-use-region ()
-  "A value for hook `self-insert-uses-region-functions'."
+  "A value for `self-insert-uses-region-functions'."
   (and (use-region-p)
-       (racket--hash-lang-lookup-pair)
+       (racket--hash-lang-lookup-pair last-command-event (1+ (point)))
        t))
 
-(defun racket--hash-lang-plain-self-insert (char)
-  "Use `self-insert-command' to insert CHAR without more pair checking."
-  (let ((racket-hash-lang-pairs nil)    ;don't recur!
-        (blink-matching-paren nil)
-        (last-command-event char))
-    (self-insert-command 1)))
-
 (defun racket-hash-lang-post-self-insert ()
-  "A value for hook `post-self-insert-hook'."
-  (pcase (racket--hash-lang-lookup-pair)
-    (`(,open-char . ,close-char)
+  "A value for `post-self-insert-hook'."
+  (pcase (racket--hash-lang-lookup-pair last-command-event (point))
+    (`(,open . ,close)
      (if (use-region-p)
          (if (<= (point) (mark))
-             (save-excursion
+             (progn
                (goto-char (mark))
-               (racket--hash-lang-plain-self-insert close-char))
+               (insert close))
+           ;; Delete open already inserted after region
+           (delete-char (- (length open)))
+           (insert close)
            (save-excursion
-             (let ((end (point)))
-               (delete-char -1)       ;delete open-char at end
-               (goto-char (mark))
-               (racket--hash-lang-plain-self-insert open-char)
-               (goto-char end)
-               (racket--hash-lang-plain-self-insert close-char))))
+             (goto-char (mark))
+             (insert open)))
        (save-excursion
-         (racket--hash-lang-plain-self-insert close-char))))))
+         (insert close))))))
 
 (defun racket-hash-lang-delete-backward-char ()
-  "Delete previous character, and following character when matching pair."
+  "Delete previous character, and possibly paired delimiters.
+
+When point immediately follows text matching the longest open
+delimiter string in `racket-hash-lang-pairs`, delete that. When
+point also immediately precedes the matching close, also delete
+that."
   (interactive)
-  (pcase (assq (char-before) racket-hash-lang-pairs)
-    (`(,_open . ,close)
-     (when (eq close (char-after))
-       (delete-char 1))))
-  (delete-char -1))
+  (pcase (racket--hash-lang-lookup-pair (char-before) (point) t)
+    (`(,open . ,close)
+     (when (equal close
+                  (buffer-substring-no-properties (point) (+ (point) (length 
close))))
+       (save-excursion (delete-char (length close))))
+     (delete-char (- (length open))))
+    (_ (delete-char -1))))
+
 (put 'racket-hash-lang-delete-backward-char 'delete-selection 'supersede)
 
 ;;; Fill



reply via email to

[Prev in Thread] Current Thread [Next in Thread]