diff --git a/extensions/smart-parens-mode/README.org b/extensions/smart-parens-mode/README.org new file mode 100644 index 000000000..a628c42f4 --- /dev/null +++ b/extensions/smart-parens-mode/README.org @@ -0,0 +1,4 @@ +* Smart Parens Mode + +** Future Features +- When selecting text and adding a opening character, close the pair character at after the text selected diff --git a/extensions/smart-parens-mode/lem-smart-parens-mode.asd b/extensions/smart-parens-mode/lem-smart-parens-mode.asd new file mode 100644 index 000000000..b5341d161 --- /dev/null +++ b/extensions/smart-parens-mode/lem-smart-parens-mode.asd @@ -0,0 +1,4 @@ +(defsystem "lem-smart-parens-mode" + :serial t + :depends-on ("lem/core") + :components ((:file "smart-parens-mode"))) diff --git a/extensions/smart-parens-mode/smart-parens-mode.lisp b/extensions/smart-parens-mode/smart-parens-mode.lisp new file mode 100644 index 000000000..4abbb3745 --- /dev/null +++ b/extensions/smart-parens-mode/smart-parens-mode.lisp @@ -0,0 +1,93 @@ +(defpackage :lem-smart-parens-mode + (:use :cl :lem) + (:export :smart-parens-mode)) +(in-package :lem-smart-parens-mode) + +(define-minor-mode smart-parens-mode + (:name "smart-parens-mode" + :keymap *smart-parens-keymap*)) + +;;; From paredit +(defun integer-char-p (char) + (< (char-code #\0) (char-code char) (char-code #\9))) + +(defun sharp-literal-p (char point) + (with-point ((p point)) + (character-offset p -1) + (and (character-at p) + (char-equal (character-at p) char) + (eql (character-at p -1) #\#)))) + +(defun sharp-n-literal-p (char point) + (with-point ((p point)) + (character-offset p -1) + (when (char-equal char (character-at p)) + (character-offset p -1) + (skip-chars-backward p #'integer-char-p) + (and (integer-char-p (character-at p)) + (eql (character-at p -1) #\#))))) + +(defparameter *non-space-following-chars* + '(#\Space #\( #\' #\` #\, #\[ #\{)) + +(defparameter *non-space-preceding-chars* + '(#\Space #\) #\] #\})) + +(defun non-space-following-context-p (&optional (p (current-point))) + (or (start-line-p p) + (find (character-at p -1) + *non-space-following-chars*) + (eql (character-at p -1) #\#) + (and (eql (character-at p -1) #\@) + (eql (character-at p -2) #\,)) + (sharp-literal-p #\' p) + (sharp-literal-p #\. p) + (sharp-literal-p #\S p) + (sharp-literal-p #\C p) + (sharp-literal-p #\+ p) + (sharp-literal-p #\- p) + (sharp-n-literal-p #\A p) + (sharp-n-literal-p #\= p))) + +(defun editor-insert-pair (open close) + (let ((p (current-point))) + (cond ((in-string-or-comment-p p) + (insert-character p open)) + ((syntax-escape-point-p p 0) + (insert-character p open)) + (t + (unless (non-space-following-context-p p) + (insert-character p #\Space)) + (insert-character p open) + (insert-character p close) + (unless (or (end-line-p p) (find (character-at p) *non-space-preceding-chars*)) + (insert-character p #\Space) + (character-offset p -1)) + (character-offset p -1))))) + +;;; smart parens specific code + +(define-command smart-parens-insert-paren () () + (editor-insert-pair #\( #\))) + +(define-command smart-parens-insert-bracket () () + (editor-insert-pair #\[ #\])) + +(define-command smart-parens-insert-brace () () + (editor-insert-pair #\{ #\})) + +(define-command smart-parens-insert-double-quote () () + (editor-insert-pair #\" #\")) + +(define-command smart-parens-insert-single-quote () () + (editor-insert-pair #\' #\')) + +(defun close-paren (given-char) + (when (lem:syntax-open-paren-char-p given-char) + (apply #'editor-insert-pair (lem:syntax-open-paren-char-p given-char)))) + +(define-key *smart-parens-keymap* "\"" 'smart-parens-insert-double-quote) +(define-key *smart-parens-keymap* "'" 'smart-parens-insert-single-quote) +(define-key *smart-parens-keymap* "(" 'smart-parens-insert-paren) +(define-key *smart-parens-keymap* "[" 'smart-parens-insert-bracket) +(define-key *smart-parens-keymap* "{" 'smart-parens-insert-brace) diff --git a/lem.asd b/lem.asd index 09702b818..9a5515a7d 100644 --- a/lem.asd +++ b/lem.asd @@ -279,6 +279,7 @@ "lem-lua-mode" #-os-windows "lem-terminal" "lem-legit" + "lem-smart-parens-mode" "lem-dashboard" "lem-copilot"))