[slime-cvs] CVS slime/contrib

CVS User nsiivola nsiivola at common-lisp.net
Thu Jun 9 11:57:34 UTC 2011


Update of /project/slime/cvsroot/slime/contrib
In directory common-lisp.net:/tmp/cvs-serv12105/contrib

Modified Files:
	ChangeLog slime-cl-indent.el 
Log Message:
slime-indentation: named Common Lisp styles

 I need to edit files with somewhat different Common Lisp indentation style
 regularly, and I doubt I'm the only one.

 `define-common-lisp-style' can be used to define new styles. If you define
 one that you use in an open-source project, please consider submitting it
 for inclusion.

     * Styles can inherit from each other.

     * Styles can specify buffer local variables.

     * Styles can have custom indentation rules.

     * Styles activation can evaluate code, so they can turn on minor modes,
       etc.

  A style can be specified for a while by doing:

     ;; -*- common-lisp-style: stylename -*=

  Some predefined styles:

     * basic: provides defaults

     * classic: emulates the 1995 vintage indentation from old
       slime-indentation

     * modern: a good pick. Turns on lambda-list smarts, but keeps more
       conservative loop indentation.

     * sbcl: SBCL style. Adds indentation for some SBCL bootstrapping
       constructs, sets indent-tabs-mode to nil, etc.


--- /project/slime/cvsroot/slime/contrib/ChangeLog	2011/06/08 21:04:53	1.466
+++ /project/slime/cvsroot/slime/contrib/ChangeLog	2011/06/09 11:57:34	1.467
@@ -1,5 +1,17 @@
 2011-06-09  Nikodemus Siivola  <nikodemus at random-state.net>
 
+	Support for named styles.
+
+	* slime-cl-indent.el (common-lisp-style)
+	(common-lisp-style-default, common-lisp-active-style)
+	(common-lisp-lisp-mode-hook, common-lisp-styles)
+	(common-lisp-add-style, define-common-lisp-style)
+	(common-lisp-set-style, common-lisp-set-style-history)
+	(common-lisp-activate-style, common-lisp-get-indentation): New
+	functions and variables. Setting `common-lisp-style' buffer local
+	variable in a file picks a style -- or use `common-lisp-set-style"
+	to do it. Indentation from style takes precedence.
+
 	Indirect indentation specs.
 
 	* slime-cl-indent.el (common-lisp-indent-function)
--- /project/slime/cvsroot/slime/contrib/slime-cl-indent.el	2011/06/08 21:04:53	1.28
+++ /project/slime/cvsroot/slime/contrib/slime-cl-indent.el	2011/06/09 11:57:34	1.29
@@ -37,7 +37,6 @@
   "Indentation in Lisp."
   :group 'lisp)
 
-
 (defcustom lisp-indent-maximum-backtracking 3
   "Maximum depth to backtrack out from a sublist for structured indentation.
 If this variable is 0, no backtracking will occur and forms such as `flet'
@@ -168,14 +167,296 @@
 This applies when the value of the `common-lisp-indent-function' property
 is set to `defun'.")
 
+
+;;;; Named styles.
+;;;;
+;;;; -*- common-lisp-style: foo -*-
+;;;;
+;;;; sets the style for the buffer.
+;;;;
+;;;; A Common Lisp style is a list of the form:
+;;;;
+;;;;  (NAME VARIABLES TABLE HOOKS DOCSTRING)
+;;;;
+;;;; where NAME is a symbol naming the style, VARIABLES is an alist specifying
+;;;; buffer local variables for the style, and TABLE is a hashtable specifying
+;;;; non-standard indentations for Common Lisp symbols. HOOKS is a list of
+;;;; functions to call when activating the style. DOCSTRING is the
+;;;; documentation for the style.
+;;;;
+;;;; `common-lisp-style' stores the name of the current style.
+;;;;
+;;;; `common-lisp-default-style' stores the name of the style to use when none
+;;;; has been specified.
+;;;;
+;;;; `common-lisp-active-style' stores the list specifying the current style.
+;;;; Whenever we're indenting, we check that they match -- and update the
+;;;; latter to match the former if necessary.
+;;;;
+;;;; Hence just setting the buffer local common-lisp-style will be enough
+;;;; to have the style take effect. `common-lisp-set-style' can also be called
+;;;; explicitly, however.
+
+(defvar common-lisp-style nil)
+
+;;; `define-common-lisp-style' updates the docstring of `common-lisp-style', using
+;;; this as the base.
+(put 'common-lisp-style 'common-lisp-style-base-doc
+     "Name of the Common Lisp indentation style used in the current buffer.
+Set this by giving eg.
+
+  ;; -*- common-lisp-style: sbcl -*-
+
+in the first line of the file, or by calling `common-lisp-set-style'. If
+buffer has no style specified, but `common-lisp-style-default' is set, that
+style is used instead. Use `define-common-lisp-style' to define new styles.")
+
+(defcustom common-lisp-style-default nil
+    "Name of the Common Lisp indentation style to use in lisp-mode buffers if
+none has been specified."
+  :type 'string
+  :group 'lisp-indent)
+
+;;; Common Lisp indentation style specification for the current buffer.
+(defvar common-lisp-active-style nil)
+
+;;; `lisp-mode' kills all buffer-local variables. Setting the
+;;; `permanent-local' property allows us to retain the style.
+(put 'common-lisp-style 'permanent-local t)
+
+;;; If style is being used, that's a sufficient invitation to snag
+;;; the indentation function.
+(defun common-lisp-lisp-mode-hook ()
+  (let ((style (or common-lisp-style common-lisp-style-default)))
+    (when style
+      (set (make-local-variable 'lisp-indent-function)
+           'common-lisp-indent-function)
+      (common-lisp-set-style style))))
+(add-hook 'lisp-mode-hook 'common-lisp-lisp-mode-hook)
+
+;;; Common Lisp indentation style specifications.
+(defvar common-lisp-styles (make-hash-table :test 'equal))
+
+(defun common-lisp-add-style (stylename base variables indentation hooks documentation)
+  (let* ((style (or (gethash stylename common-lisp-styles)
+                    (let ((new (list (intern stylename)             ; name
+                                     nil                            ; variable bindings
+                                     (make-hash-table :test 'equal) ; indentation table
+                                     nil                            ; hooks
+                                     nil)))                         ; docstring
+                      (puthash stylename new common-lisp-styles)
+                      new)))
+         (base-style (when base
+                       (or (gethash base common-lisp-styles)
+                           (error "Unknown base Common Lisp style: %s" base))))
+         (base-vars (second base-style))
+         (base-methods (third base-style))
+         (base-hooks (fourth base-style)))
+    ;; Variables
+    (setf (second style) variables)
+    (dolist (var base-vars)
+      (unless (assoc (car var) variables)
+        (push var (second style))))
+    ;; Indentation
+    (let ((methods (third style)))
+      (clrhash methods)
+      (when base-methods
+        (maphash (lambda (s m)
+                   (puthash s m methods))
+                 base-methods))
+      (dolist (indent indentation)
+        (let* ((name (car indent))
+               (spec (cdr indent))
+               (method
+                (if (symbolp spec)
+                    (list :as spec)
+                  (when (cdr spec)
+                    (error "Malformed Common Lisp indentation spec: %s" indent))
+                  (car spec))))
+          (puthash name method methods))))
+    ;; Hooks
+    (setf (fourth style) nil)
+    (dolist (hook (reverse base-hooks))
+      (unless (member hook hooks)
+        (push hook (fourth style))))
+    (dolist (hook (reverse hooks))
+      (push hook (fourth style)))
+    ;; Documentation
+    (setf (fifth style) documentation)
+    ;; Frob `common-lisp-style' docstring.
+    (let ((doc (get 'common-lisp-style 'common-lisp-style-base-doc))
+          (all nil))
+      (setq doc (concat doc "\n\nAvailable styles are:\n"))
+      (maphash (lambda (name style)
+                 (push (list name (fifth style)) all))
+               common-lisp-styles)
+      (dolist (info (sort all (lambda (a b) (string< (car a) (car b)))))
+        (let ((style-name (first info))
+              (style-doc (second info)))
+          (if style-doc
+              (setq doc (concat doc
+                                "\n " style-name "\n"
+                                "   " style-doc "\n"))
+                     (setq doc (concat doc
+                                       "\n " style-name " (undocumented)\n")))))
+      (put 'common-lisp-style 'variable-documentation doc))
+    stylename))
+
+(defmacro define-common-lisp-style (name documentation &rest options)
+  "Define a Common Lisp indentation style.
+
+NAME is the name of the style.
+
+DOCUMENTATION is the docstring for the style, automatically added to the
+docstring of `common-lisp-style'.
+
+OPTIONS are:
+
+ (:variables (name value) ...)
+
+  Specifying the buffer local variables associated with the style.
+
+ (:indentation (symbol spec) ...)
+
+  Specifying custom indentations associated with the style. SPEC is
+  a normal `common-lisp-indent-function' indentation specification.
+
+ (:inherit style)
+
+  Inherit variables and indentations from another Common Lisp style.
+
+ (:eval form ...)
+
+  Lisp code to evaluate when activating the style. This can be used to
+  eg. activate other modes.
+"
+  (when (consp documentation)
+    (setq documentation nil
+          options (cons documentation options)))
+  `(common-lisp-add-style ,name
+                          ',(cadr (assoc :inherit options))
+                          ',(cdr (assoc :variables options))
+                          ',(cdr (assoc :indentation options))
+                          ,(when (assoc :eval options)
+                            `(list
+                              (lambda ()
+                                ,@(cdr (assoc :eval options)))))
+                          ,documentation))
+
+(define-common-lisp-style "basic"
+  "This style merely gives all identation variables their default values,
+   making it easy to create new styles that are proof against user
+   customizations."
+  (:variables
+   (lisp-indent-maximum-backtracking 3)
+   (lisp-tag-indentation 1)
+   (lisp-tag-body-indentation 3)
+   (lisp-backquote-indentation t)
+   (lisp-loop-indent-subclauses t)
+   (lisp-loop-indent-forms-like-keywords nil)
+   (lisp-simple-loop-indentation 2)
+   (lisp-align-keywords-in-calls t)
+   (lisp-lambda-list-indentation t)
+   (lisp-lambda-list-keyword-alignment nil)
+   (lisp-lambda-list-keyword-parameter-indentation 2)
+   (lisp-lambda-list-keyword-parameter-alignment nil)
+   (lisp-indent-defun-method (4 &lambda &body))))
+
+(define-common-lisp-style "classic"
+  "This style of indentation emulates the most striking features of 1995
+   vintage cl-indent.el once included as part of Slime: IF indented by two
+   spaces, and CASE clause bodies indentented more deeply than the keys."
+  (:inherit "basic")
+  (:variables
+   (lisp-lambda-list-keyword-parameter-indentation 0))
+  (:indentation
+   (case (4 &rest (&whole 2 &rest 3)))
+   (if   (4 2 2))))
+
+(define-common-lisp-style "modern"
+  "A good general purpose style. Turns on lambda-list keyword and keyword
+   parameter alignment, and turns subclause aware loop indentation off.
+   (Loop indentation so because simpler style is more prevalent in existing
+   sources, not because it is necessarily preferred.)"
+  (:inheric "basic")
+  (:variables
+   (lisp-lambda-list-keyword-alignment t)
+   (lisp-lambda-list-keyword-parameter-alignment t)
+   (lisp-lambda-list-keyword-parameter-indentation 0)
+   (lisp-loop-indent-subclauses nil)))
+
+(define-common-lisp-style "sbcl"
+  "Style used in SBCL sources. A good if somewhat intrusive general purpose
+   style based on the \"modern\" style. Adds indentation for a few SBCL
+   specific constructs, sets indentation to use spaces instead of tabs,
+   fill-column to 78, and activates whitespace-mode to show tabs and trailing
+   whitespace."
+  (:inherit "modern")
+  (:eval
+   (whitespace-mode 1))
+  (:variables
+   (whitespace-style (tabs trailing))
+   (indent-tabs-mode nil)
+   (comment-fill-column nil)
+   (fill-column 78))
+  (:indentation
+   (def!constant       (:as defconstant))
+   (def!macro          (:as defmacro))
+   (def!method         (:as defmethod))
+   (def!struct         (:as defstruct))
+   (def!type           (:as deftype))
+   (defmacro-mundanely (:as defmacro))))
+
+(defvar common-lisp-set-style-history nil)
+
+(defun common-lisp-set-style (stylename)
+  "Set current buffer to use the Common Lisp style STYLENAME.
+STYLENAME, a string, must be an existing Common Lisp style. Styles
+are added (and updated) using `common-lisp-add-style'.
+
+The buffer-local variable `common-lisp-style' will get set to STYLENAME.
+Simply setting
+
+A Common Lisp style is composed of variable and indentation specifications."
+  (interactive
+   (list (let ((completion-ignore-case t)
+               (prompt "Specify Common Lisp indentation style: "))
+           (completing-read prompt
+                            common-lisp-styles nil t nil
+                            'common-lisp-set-style-history))))
+  (let* ((stylename (if (stringp stylename)
+                        stylename
+                      (symbol-name stylename)))
+         (style (or (gethash stylename common-lisp-styles)
+                    (error "Unknown Common Lisp style: %s" stylename))))
+    (common-lisp-activate-style style)))
+
+(defun common-lisp-activate-style (style)
+  (let ((vars (second style))
+        (methods (third style))
+        (hooks (fourth style)))
+    (dolist (var vars)
+      (set (make-local-variable (car var)) (cadr var)))
+    (dolist (hook hooks)
+      (funcall hook))
+    (set (make-local-variable 'common-lisp-style) (car style))
+    (set (make-local-variable 'common-lisp-active-style) style)))
+
+;;;; The indentation specs are stored at two levels: the global defaults
+;;;; live in symbol properties: how to indent `foo' is stored in the
+;;;; `common-lisp-indent-function' property of `foo'.
+;;;;
+;;;; When a specific style is in use, we first look in its method table.
 (defun common-lisp-get-indentation (name)
   "Retrieves the indentation information for NAME."
   (let ((method
-         (get name 'common-lisp-indent-function)))
+         (or (when common-lisp-active-style
+               (gethash name (third common-lisp-active-style)))
+             (get name 'common-lisp-indent-function))))
     (if (and (consp method) (eq 'as (car method)))
         (common-lisp-get-indentation (cadr method))
       method)))
-
+
 ;;;; LOOP indentation, the simple version
 
 (defun common-lisp-loop-type (loop-start)





More information about the slime-cvs mailing list