File: swank-indentation.lisp

package info (click to toggle)
slime 1%3A20120525-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 4,696 kB
  • sloc: lisp: 40,236; ruby: 321; sh: 161; makefile: 129; awk: 10
file content (140 lines) | stat: -rw-r--r-- 5,845 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
(in-package :swank)

(defvar *application-hints-tables* '()
  "A list of hash tables mapping symbols to indentation hints (lists 
of symbols and numbers as per cl-indent.el). Applications can add hash 
tables to the list to change the auto indentation slime sends to 
emacs.")

(defun has-application-indentation-hint-p (symbol)
  (let ((default (load-time-value (gensym))))
    (dolist (table *application-hints-tables*)
      (let ((indentation (gethash symbol table default)))
        (unless (eq default indentation)
          (return-from has-application-indentation-hint-p
            (values indentation t))))))
  (values nil nil))

(defun application-indentation-hint (symbol)
  (let ((indentation (has-application-indentation-hint-p symbol)))
    (labels ((walk (indentation-spec)
               (etypecase indentation-spec
                 (null nil)
                 (number indentation-spec)
                 (symbol (string-downcase indentation-spec))
                 (cons (cons (walk (car indentation-spec))
                             (walk (cdr indentation-spec)))))))
      (walk indentation))))

;;; override swank version of this function
(defun symbol-indentation (symbol)
  "Return a form describing the indentation of SYMBOL. 

The form is to be used as the `common-lisp-indent-function' property 
in Emacs."
  (cond
    ((has-application-indentation-hint-p symbol)
     (application-indentation-hint symbol))
    ((and (macro-function symbol)
             (not (known-to-emacs-p symbol)))
     (let ((arglist (arglist symbol)))
       (etypecase arglist
         ((member :not-available)
          nil)
         (list
          (macro-indentation arglist)))))
    (t nil)))

;;; More complex version.
(defun macro-indentation (arglist)
  (labels ((frob (list &optional base)
             (if (every (lambda (x)
                          (member x '(nil "&rest") :test #'equal))
                        list)
                 ;; If there was nothing interesting, don't return anything.
                 nil
                 ;; Otherwise substitute leading NIL's with 4 or 1.
                 (let ((ok t))
                   (substitute-if (if base
                                      4
                                      1)
                                  (lambda (x)
                                    (if (and ok (not x))
                                        t
                                        (setf ok nil)))
                                  list))))
           (walk (list level &optional firstp)
             (when (consp list)
               (let ((head (car list)))
                 (if (consp head)
                     (let ((indent (frob (walk head (+ level 1) t))))
                       (cons (list* "&whole" (if (zerop level)
                                                 4
                                                 1)
                                    indent) (walk (cdr list) level)))
                     (case head
                       ;; &BODY is &BODY, this is clear.
                       (&body
                        '("&body"))
                       ;; &KEY is tricksy. If it's at the base level, we want
                       ;; to indent them normally:
                       ;;
                       ;;  (foo bar quux
                       ;;       :quux t
                       ;;       :zot nil)
                       ;;
                       ;; If it's at a destructuring level, we want indent of 1:
                       ;;
                       ;;  (with-foo (var arg
                       ;;             :foo t
                       ;;             :quux nil)
                       ;;     ...)
                       (&key
                        (if (zerop level)
                            '("&rest" nil)
                            '("&rest" 1)))
                       ;; &REST is tricksy. If it's at the front of
                       ;; destructuring, we want to indent by 1, otherwise
                       ;; normally:
                       ;;
                       ;;  (foo (bar quux
                       ;;        zot)
                       ;;    ...)
                       ;;
                       ;; but
                       ;;
                       ;;  (foo bar quux
                       ;;       zot)
                       (&rest
                        (if (and (plusp level) firstp)
                            '("&rest" 1)
                            '("&rest" nil)))
                       ;; &WHOLE and &ENVIRONMENT are skipped as if they weren't there
                       ;; at all.
                       ((&whole &environment)
                        (walk (cddr list) level firstp))
                       ;; &OPTIONAL is indented normally -- and the &OPTIONAL marker
                       ;; itself is not counted.
                       (&optional
                        (walk (cdr list) level))
                       ;; Indent normally, walk the tail -- but
                       ;; unknown lambda-list keywords terminate the walk.
                       (otherwise
                        (unless (member head lambda-list-keywords)
                          (cons nil (walk (cdr list) level))))))))))
    (frob (walk arglist 0 t) t)))

#+nil
(progn
  (assert (equal '(4 4 ("&whole" 4 "&rest" 1) "&body")
                 (macro-indentation '(bar quux (&rest slots) &body body))))
  (assert (equal nil
                 (macro-indentation '(a b c &rest more))))
  (assert (equal '(4 4 4 "&body")
                 (macro-indentation '(a b c &body more))))
  (assert (equal '(("&whole" 4 1 1 "&rest" 1) "&body")
                 (macro-indentation '((name zot &key foo bar) &body body))))
  (assert (equal nil
                 (macro-indentation '(x y &key z)))))

(provide :swank-indentation)