[climacs-cvs] CVS climacs

thenriksen thenriksen at common-lisp.net
Wed Jan 9 09:47:16 UTC 2008


Update of /project/climacs/cvsroot/climacs
In directory clnet:/tmp/cvs-serv9361

Modified Files:
	climacs.asd 
Added Files:
	structured-editing.lisp 
Log Message:
Added Structedit Mode to Climacs.


--- /project/climacs/cvsroot/climacs/climacs.asd	2008/01/07 23:08:14	1.65
+++ /project/climacs/cvsroot/climacs/climacs.asd	2008/01/09 09:47:16	1.66
@@ -41,6 +41,7 @@
 ;;    (:file "ttcn3-syntax" :depends-on ("packages"))
    (:file "climacs-lisp-syntax" :depends-on ("core" "groups"))
    (:file "climacs-lisp-syntax-commands" :depends-on ("climacs-lisp-syntax" "misc-commands"))
+   (:file "structured-editing" :depends-on ("climacs-lisp-syntax-commands"))
    (:file "c-syntax" :depends-on ("core"))
    (:file "c-syntax-commands" :depends-on ("c-syntax" "misc-commands"))
    #+nil(:file "java-syntax" :depends-on ("core"))

--- /project/climacs/cvsroot/climacs/structured-editing.lisp	2008/01/09 09:47:16	NONE
+++ /project/climacs/cvsroot/climacs/structured-editing.lisp	2008/01/09 09:47:16	1.1
;;; -*- Mode: Lisp; Package: CLIMACS-STRUCTEDIT -*-

;;;  (c) copyright 2008 by
;;;           Troels Henriksen (athas at sigkill.dk)
;;;
;;; This library is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Library General Public
;;; License as published by the Free Software Foundation; either
;;; version 2 of the License, or (at your option) any later version.
;;;
;;; This library 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
;;; Library General Public License for more details.
;;;
;;; You should have received a copy of the GNU Library General Public
;;; License along with this library; if not, write to the
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;;; Boston, MA  02111-1307  USA.

;;; Implementation of structural editing commands for the Lisp syntax
;;; in Climacs. These commands were inspired by Paredit, which was
;;; originally written by Taylor "Riastradh" Campbell for GNU
;;; Emacs. In particular, many docstrings have been copied verbatim,
;;; then modified.

;;; This is a work in progress, be aware that problems are likely to
;;; arise, and that the editing commands are not yet completely
;;; comprehensive. Patches are, of course, welcome.

;;; You must do M-x Structedit Mode to enable these commands.

(defpackage :climacs-structedit
  (:use :clim-lisp :clim :esa :esa-utils :drei :drei-buffer :drei-base :drei-core
        :drei-motion :drei-editing :drei-syntax :drei-lr-syntax :drei-lisp-syntax)
  (:shadow clim:form))

(in-package :climacs-structedit)

(define-syntax-mode structedit-mode ()
  ()
  (:documentation "A mode for Paredit-style editing in Lisp syntax.")
  (:applicable-syntaxes lisp-syntax))

(define-mode-toggle-commands com-structedit-mode (structedit-mode "Structedit")
  :command-table lisp-table)

(make-command-table 'structedit-table
 :errorp nil)

(defmethod syntax-command-tables append ((syntax structedit-mode))
  '(structedit-table))

(defun delete-form (buffer form)
  "Delete `form' from `buffer'."
  (delete-buffer-range
   buffer (start-offset form) (size form)))

(define-command (com-open-list :name t :command-table structedit-table)
    ((n 'integer :default 0))
  "Insert a balanced parenthesis pair.
With an argument N, put the closing parentheses after N
S-expressions forward.  If in string or comment, insert a single
opening parenthesis.  If in a character literal, replace the
character literal with #\(."
  (cond ((in-string-p (current-syntax) (point))
         (insert-character #\())
        ((in-comment-p (current-syntax) (point))
         (insert-character #\())
        ((in-character-p (current-syntax) (point))
         (delete-form (current-buffer) (form-around (current-syntax) (offset (point))))
         (insert-sequence (point) "#\\("))
        (t
         (when (and (not (zerop n))
                    (forward-expression (point) (current-syntax) 1 nil))
           (backward-expression (point) (current-syntax) 1 nil))
         (insert-character #\()
         (forward-expression (point) (current-syntax) n nil)
         (insert-character #\))
         (backward-object (point))
         (backward-expression (point) (current-syntax) n nil))))

(define-command (com-wrap-expression :name t :command-table structedit-table)
    ((n 'integer :default 1))
  "Wrap the following N S-expressions in a list.
Automatically indent the newly wrapped S-expressions.  As a
special case, if the point is at the end of a list, simply insert
a pair of parentheses, rather than insert a lone opening
parenthesis and then signal an error, in the interest of
preserving structural validity."
  (com-open-list n))

(define-command (com-close-list-and-newline :name t :command-table structedit-table)
    ()
  "Move past one closing delimiter, add a newline, and reindent."
  (cond ((or (in-string-p (current-syntax) (point))
             (in-comment-p (current-syntax) (point)))
         (insert-character #\)))
        ((in-character-p (current-syntax) (point))
         (delete-form (current-buffer) (form-around (current-syntax) (offset (point))))
         (insert-sequence (point) "#\\)"))
        ((forward-up (point) (current-syntax) 1 nil)
         (insert-object (point) #\Newline)
         (indent-current-line (current-view) (point)))))

(defun delete-object-structurally (delete-fn move-fn immediate-form-fn
                                   border-offset-fn
                                   at-border-fn)
  "Delete an object at `(point)' structurally. `Delete-fn' is
either `forward-delete-object' or `backward-delete-object',
`move-fn' is either `forward-object' or `backward-object',
`immediate-form-fn' is some form selector, `border-offset-fn' is
either `end-offset' or `begin-offset', `at-border-fn' is a
function used to determine whether or not `(point)' is at the end
of a structural object."
  (let ((immediate-form (funcall immediate-form-fn (current-syntax) (offset (point))))
        (form-around (form-around (current-syntax) (offset (point)))))
    (cond ((and (or (form-string-p immediate-form)
                    (form-list-p immediate-form))
                (= (funcall border-offset-fn immediate-form)
                   (offset (point))))
           (funcall move-fn (point)))
          ((funcall at-border-fn (current-syntax) (point))
           (when (null (form-children (list-at-mark (current-syntax) (point))))
             (delete-form (current-buffer) form-around)))
          ((and (form-character-p immediate-form)
                (= (funcall border-offset-fn immediate-form)
                   (offset (point))))
           (delete-form (current-buffer) immediate-form))
          (t (funcall delete-fn (point))))))

(define-command (com-forward-delete-object-structurally
                 :name t :command-table structedit-table)
    ((force 'boolean :default nil))
  "Delete a character forward or move forward over a delimiter.
If on an opening S-expression delimiter, move forward into the
S-expression. If on a closing S-expression delimiter, refuse to
delete unless the S-expression is empty, in which case delete the
whole S-expression. If `force' is true, simply delete a character
forward, without regard for delimiter balancing."
  (if force
      (forward-delete-object (point))
      (delete-object-structurally #'forward-delete-object #'forward-object
                                  #'form-after #'start-offset
                                  #'location-at-end-of-form)))

(define-command (com-backward-delete-object-structurally
                 :name t :command-table structedit-table)
    ((force 'boolean :default nil))
  "Delete a character backward or move backward over a delimiter.
If on an ending S-expression delimiter, move backward into the
S-expression. If on an opening S-expression delimiter, refuse to
delete unless the S-expression is empty, in which case delete the
whole S-expression. If `force' is true, simply delete a
character backward, without regard for delimiter balancing."
  (if force
      (backward-delete-object (point))
      (delete-object-structurally #'backward-delete-object #'backward-object
                                  #'form-before #'end-offset
                                  #'location-at-beginning-of-form)))

(define-command (com-insert-double-quote-structurally
                 :name t :command-table structedit-table)
    ((n 'integer :default 0))
  "Insert a pair of double-quotes.
With a prefix argument N, wrap the following N S-expressions in
  double-quotes, escaping intermediate characters if necessary.
Inside a comment, insert a literal double-quote.
At the end of a string, move past the closing double-quote.
In the middle of a string, insert a backslash-escaped double-quote.
If in a character literal, replace the character literal with #\\\"."
  (cond ((in-comment-p (current-syntax) (point))
         (insert-character #\"))
        ((at-end-of-string-p (current-syntax) (point))
         (forward-object (point)))
        ((in-string-p (current-syntax) (point))
         (insert-sequence (point) "\\\""))
        ((in-character-p (current-syntax) (point))
         (delete-form (current-buffer) (form-around (current-syntax) (offset (point))))
         (insert-sequence (point) "#\\\""))
        (t
         (let ((old-offset (offset (point))))
           (forward-expression (point) (current-syntax) n nil)
           (insert-buffer-object (current-buffer) old-offset #\")
           (insert-character #\")
           (backward-object (point))
           (backward-expression (point) (current-syntax) (min 1 n) nil)))))

(define-command (com-wrap-expression-in-doublequote :name t :command-table structedit-table)
    ((n 'integer :default 1))
  "Move to the end of the string, insert a newline, and indent.
If not in a string, act as `Insert Double Quote Structurally'; if
no prefix argument is specified, the default is to wrap one
S-expression, however, not zero."
  (if (in-string-p (current-syntax) (point))
      (setf (offset (point))
            (1+ (end-offset (form-around (current-syntax) (point)))))
      (com-insert-double-quote-structurally n)))

(define-command (com-splice-list :name t :command-table structedit-table)
    ((kill-backward 'boolean :default nil))
  "Splice the list that the point is on by removing its delimiters.
With a prefix argument as in `C-u', kill all S-expressions
backward in the current list before splicing all S-expressions
forward into the enclosing list."
  (let ((list (list-at-mark (current-syntax) (point))))
    (when list
      (let ((begin-mark (make-buffer-mark (current-buffer) (start-offset list)))
            (end-mark (make-buffer-mark (current-buffer) (end-offset list))))
        (when kill-backward
          (loop until (eq (list-at-mark (current-syntax) (offset (point)))
                          (or (form-before (current-syntax) (offset (point)))
                              (form-around (current-syntax) (offset (point)))))
             do (backward-delete-expression (point) (current-syntax) 1 nil)))
        (delete-buffer-range (current-buffer) (offset begin-mark) 1)
        (delete-buffer-range (current-buffer) (1- (offset end-mark)) 1)))))

(define-command (com-kill-line-structurally :name t :command-table structedit-table)
    ()
  "Kill a line as if with \"Kill Line\", but respecting delimiters.
In a string, act exactly as \"Kill Line\" but do not kill past
the closing string delimiter.  On a line with no S-expressions on
it starting after the point or within a comment, act exactly as
\"Kill Line\".  Otherwise, kill all S-expressions that start
after the point."
  (let ((form-around (form-around (current-syntax) (offset (point))))
        (form-after (form-after (current-syntax) (offset (point))))
        (comment (comment-at-mark (current-syntax) (point))))
    (cond ((empty-line-p (point))
           (forward-delete-object (point)))
          ((in-string-p (current-syntax) (point))
           (if (= (buffer-line-number (current-buffer) (end-offset form-around))
                  (line-number (point)))
               ;; Delete from point until the end of the string, but
               ;; keep the ending delimiter.
               (kill-region (point) (1- (end-offset form-around)))
               ;; Delete from point until end of line.
               (kill-region (point) (end-of-line (clone-mark (point))))))
          ((in-line-comment-p (current-syntax) (point))
           ;; Delete until end of line
           (kill-region (point) (end-of-line (clone-mark (point)))))
          ((in-long-comment-p (current-syntax) (point))
           (if (= (buffer-line-number (current-buffer) (end-offset comment))
                  (line-number (point)))
               ;; End of comment on same line as point, if a complete
               ;; long comment, don't delete the ending delimiter
               (kill-region (point) (- (end-offset comment)
                                       (if (form-complete-p comment)
                                           2 0)))
               ;; Delete from point until end of line.
               (kill-region (point) (end-of-line (clone-mark (point))))))
          ((= (buffer-line-number (current-buffer) (start-offset form-after))
              (line-number (point)))
           (forward-kill-expression (point) (current-syntax))
           (loop for form-after = (form-after (current-syntax) (offset (point)))
              while (and form-after
                         (= (buffer-line-number (current-buffer) (start-offset form-after))
                            (line-number (point))))
              do (forward-kill-expression (point) (current-syntax) 1 t))))))

(set-key `(com-open-list ,*numeric-argument-marker* ,*numeric-argument-marker*)
         'structedit-table
         '(#\())

(set-key `(com-wrap-expression ,*numeric-argument-marker*)
         'structedit-table
         '((#\( :meta :shift)))

(set-key 'com-close-list-and-newline
         'structedit-table
         '(#\)))

(set-key `(com-forward-delete-object-structurally ,*numeric-argument-marker*)
         'structedit-table
         '((#\d :control)))

(set-key `(com-backward-delete-object-structurally ,*numeric-argument-marker*)
         'structedit-table
         '((#\Backspace)))

(set-key `(com-insert-double-quote-structurally ,*numeric-argument-marker*)
         'structedit-table
         '((#\")))

(set-key `(com-wrap-expression-in-doublequote ,*numeric-argument-marker*)
         'structedit-table
         '((#\" :meta :shift)))

(set-key `(com-splice-list ,*numeric-argument-marker*)
         'structedit-table
         '((#\s :meta)))

(set-key 'com-kill-line-structurally
         'structedit-table
         '((#\k :control)))



More information about the Climacs-cvs mailing list