1f14f4646Smrg;; Copyright (c) 2008 Paulo Cesar Pereira de Andrade
2f14f4646Smrg;;
3f14f4646Smrg;; Permission is hereby granted, free of charge, to any person obtaining a
4f14f4646Smrg;; copy of this software and associated documentation files (the "Software"),
5f14f4646Smrg;; to deal in the Software without restriction, including without limitation
6f14f4646Smrg;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
7f14f4646Smrg;; and/or sell copies of the Software, and to permit persons to whom the
8f14f4646Smrg;; Software is furnished to do so, subject to the following conditions:
9f14f4646Smrg;;
10f14f4646Smrg;; The above copyright notice and this permission notice (including the next
11f14f4646Smrg;; paragraph) shall be included in all copies or substantial portions of the
12f14f4646Smrg;; Software.
13f14f4646Smrg;;
14f14f4646Smrg;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15f14f4646Smrg;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16f14f4646Smrg;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17f14f4646Smrg;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18f14f4646Smrg;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19f14f4646Smrg;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20f14f4646Smrg;; DEALINGS IN THE SOFTWARE.
21f14f4646Smrg;;
22f14f4646Smrg;; Author: Paulo Cesar Pereira de Andrade
23f14f4646Smrg;;
24f14f4646Smrg
25f14f4646Smrg(require "syntax")
26f14f4646Smrg(require "indent")
27f14f4646Smrg(in-package "XEDIT")
28f14f4646Smrg
29f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
30f14f4646Smrg(defsynprop *prop-indent*
31f14f4646Smrg  "indent"
32f14f4646Smrg  :font		"*courier-medium-r*-12-*"
33f14f4646Smrg  :background	"Gray92")
34f14f4646Smrg
35f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
36f14f4646Smrg(defsynoptions *python-DEFAULT-options*
37f14f4646Smrg  ;; Positive number. Basic indentation
38f14f4646Smrg  (:indentation		.	4)
39f14f4646Smrg
40f14f4646Smrg  ;; Boolean. Move cursor to the indent column after pressing <Enter>?
41f14f4646Smrg  (:newline-indent	.	t)
42f14f4646Smrg
43f14f4646Smrg  ;; Boolean. Set to T if tabs shouldn't be used to fill indentation.
44f14f4646Smrg  (:emulate-tabs	.	t)
45f14f4646Smrg
46f14f4646Smrg  ;; Boolean. Only calculate indentation after pressing <Enter>?
47f14f4646Smrg  ;;		This may be useful if the parser does not always
48f14f4646Smrg  ;;		do what the user expects...
49f14f4646Smrg  (:only-newline-indent	.	nil)
50f14f4646Smrg
51f14f4646Smrg  ;; Boolean. Remove extra spaces from previous line.
52f14f4646Smrg  ;;		This should default to T when newline-indent is not NIL.
53f14f4646Smrg  (:trim-blank-lines	.	nil)
54f14f4646Smrg
55f14f4646Smrg  ;; Boolean. If this hash-table entry is set, no indentation is done.
56f14f4646Smrg  ;;		Useful to temporarily disable indentation.
57f14f4646Smrg  (:disable-indent	.	nil))
58f14f4646Smrg
59f14f4646Smrg
60f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
61f14f4646Smrg;;   Not doing "special" indentation of multiline ( because it is attempting
62f14f4646Smrg;; to do a "smart" indentation and usually don't read more then one line
63f14f4646Smrg;; back to resolve indentation.
64f14f4646Smrg;;   Code for multiline { and [, usually declaring vector/hash like variables
65f14f4646Smrg;; should be working properly.
66f14f4646Smrg;;   Note that the indent lisp hook is only run on character additions, so
67f14f4646Smrg;; it doesn't do a "smart" tabbing when pressing backspace, but it will
68f14f4646Smrg;; properly align to the "closest tab stop" when typping a character.
69f14f4646Smrg(defindent *python-mode-indent* :main
70f14f4646Smrg  ;; this must be the first token
71f14f4646Smrg  (indtoken "^\\s*" :indent
72f14f4646Smrg    :code (or *offset* (setq *offset* (+ *ind-offset* *ind-length*))))
73f14f4646Smrg
74f14f4646Smrg  ;; ignore comments
75f14f4646Smrg  (indtoken "#.*$" nil)
76f14f4646Smrg
77f14f4646Smrg  (indtoken ":" :collon :nospec t)
78f14f4646Smrg
79f14f4646Smrg  ;; don't directly match {}, [], () strings, and :
80f14f4646Smrg  (indtoken "[a-zA-Z0-9+*/%^&<>=.,|!~-]+" :expression)
81f14f4646Smrg
82f14f4646Smrg  ;; if in the same line, reduce now, as delimiters are identical
83f14f4646Smrg  (indtoken "'([^\\']|\\\\.)*'" :expression)
84f14f4646Smrg  (indtoken "\"([^\\\"]|\\\\.)*\"" :expression)
85f14f4646Smrg  ;; otherwise, use a table
86f14f4646Smrg  (indtoken "\"" :cstring :nospec t :begin :string)
87f14f4646Smrg  (indtoken "'" :cconstant :nospec t :begin :constant)
88f14f4646Smrg  (indtoken "\"\"\"" :cstring3 :nospec t :begin :string3)
89f14f4646Smrg  (indtoken "'''" :cconstant :nospec t :begin :constant3)
90f14f4646Smrg
91f14f4646Smrg  (indinit (braces 0))
92f14f4646Smrg  (indtoken "}" :cbrace :nospec t :code (incf braces))
93f14f4646Smrg  (indtoken "{" :obrace	:nospec t :code (decf braces))
94f14f4646Smrg  (indtoken ")" :cparen :nospec t :code (incf braces))
95f14f4646Smrg  (indtoken "(" :oparen	:nospec t :code (decf braces))
96f14f4646Smrg  (indtoken "]" :cbrack	:nospec t :code (incf braces))
97f14f4646Smrg  (indtoken "[" :obrack	:nospec t :code (decf braces))
98f14f4646Smrg
99f14f4646Smrg  ;; This must be the last token
100f14f4646Smrg  (indtoken "$"		:eol)
101f14f4646Smrg
102f14f4646Smrg  (indtable :string
103f14f4646Smrg    ;; Ignore escaped characters
104f14f4646Smrg    (indtoken "\\." nil)
105f14f4646Smrg    ;; Return to the toplevel when the start of the string is found
106f14f4646Smrg    (indtoken "\"" :ostring :nospec t :switch -1))
107f14f4646Smrg  (indtable :constant
108f14f4646Smrg    (indtoken "\\." nil)
109f14f4646Smrg    (indtoken "'" :oconstant :nospec t :switch -1))
110f14f4646Smrg
111f14f4646Smrg  (indtable :string3
112f14f4646Smrg    (indtoken "\"\"\"" :ostring3 :nospec t :switch -1))
113f14f4646Smrg  (indtable :constant3
114f14f4646Smrg    (indtoken "'''" :oconstant3 :nospec t :switch -1))
115f14f4646Smrg
116f14f4646Smrg  ;; Reduce what isn't reduced in regex pattern match
117f14f4646Smrg  (indreduce :expression
118f14f4646Smrg    t
119f14f4646Smrg    ((:expression :expression)
120f14f4646Smrg      ;; multiline strings
121f14f4646Smrg      (:ostring (not :ostring) :cstring)
122f14f4646Smrg      (:oconstant (not :oconstant) :cconstant)
123f14f4646Smrg      (:ostring3 (not :ostring3) :cstring3)
124f14f4646Smrg      (:oconstant3 (not :oconstant3) :cconstant3)
125f14f4646Smrg      ;; braces, parenthesis and brackets
126f14f4646Smrg      (:obrace (not :obrace) :cbrace)
127f14f4646Smrg      (:oparen (not :oparen) :cparen)
128f14f4646Smrg      (:obrack (not :obrack) :cbrack)))
129f14f4646Smrg
130f14f4646Smrg  ;; This should be the most common exit point;
131f14f4646Smrg  ;; just copy previous line indentation.
132f14f4646Smrg  (indreduce :align
133f14f4646Smrg    (< *ind-offset* *ind-start*)
134f14f4646Smrg    ((:indent :eol)
135f14f4646Smrg      (:indent :expression :eol))
136f14f4646Smrg    (setq *indent* (offset-indentation *offset* :resolve t))
137f14f4646Smrg
138f14f4646Smrg    ;; If cursor is not in an indentation tab, assume user is trying to align
139f14f4646Smrg    ;; to another block, and just use the resolve code to round it down
140f14f4646Smrg    (unless (/= (mod *indent* *base-indent*) 0)
141f14f4646Smrg      ;; else use "previous-line" indentation.
142f14f4646Smrg      (setq *indent* (offset-indentation *ind-offset* :resolve t)))
143f14f4646Smrg    (indent-macro-reject-left))
144f14f4646Smrg
145f14f4646Smrg  ;; This should be second most common exit point;
146f14f4646Smrg  ;; add one indentation level.
147f14f4646Smrg  (indreduce :align
148f14f4646Smrg    (< *ind-offset* *ind-start*)
149f14f4646Smrg    ((:indent :expression :collon :eol))
150f14f4646Smrg    (setq *indent* (+ *base-indent* (offset-indentation *ind-offset* :resolve t)))
151f14f4646Smrg    (indent-macro-reject-left))
152f14f4646Smrg
153f14f4646Smrg  (indresolve :align
154f14f4646Smrg    (setq *indent* (- *indent* (mod *indent* *base-indent*))))
155f14f4646Smrg
156f14f4646Smrg  ;; Calculate special indentation for [ and {
157f14f4646Smrg  (indresolve (:obrack :obrace)
158f14f4646Smrg    (and
159f14f4646Smrg      (< *ind-offset* *ind-start*)
160f14f4646Smrg      (setq *indent* (+ *base-indent*
161f14f4646Smrg		       (offset-indentation *ind-offset* :resolve t)))))
162f14f4646Smrg  (indresolve (:cbrack :cbrace)
163f14f4646Smrg    (setq *indent* (- (offset-indentation *ind-offset* :resolve t)
164f14f4646Smrg		     (if (>= *ind-offset* *ind-start*)
165f14f4646Smrg		       *base-indent* 0))))
166f14f4646Smrg)
167f14f4646Smrg
168f14f4646Smrg
169f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
170f14f4646Smrg(defun python-offset-indent (&aux char (point (point)))
171f14f4646Smrg  ;; Skip spaces forward
172f14f4646Smrg  (while (member (setq char (char-after point)) indent-spaces)
173f14f4646Smrg    (incf point))
174f14f4646Smrg  point)
175f14f4646Smrg
176f14f4646Smrg(compile 'python-offset-indent)
177f14f4646Smrg
178f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
179f14f4646Smrg(defun python-should-indent (options &aux point start end offset)
180f14f4646Smrg  (when (hash-table-p options)
181f14f4646Smrg    ;; check if previous line has extra spaces
182f14f4646Smrg    (and (gethash :trim-blank-lines options)
183f14f4646Smrg      (indent-clear-empty-line))
184f14f4646Smrg
185f14f4646Smrg    ;; indentation disabled?
186f14f4646Smrg    (and (gethash :disable-indent options)
187f14f4646Smrg      (return-from python-should-indent))
188f14f4646Smrg
189f14f4646Smrg    (setq
190f14f4646Smrg      point	(point)
191f14f4646Smrg      start	(scan point :eol :left)
192f14f4646Smrg      end	(scan point :eol :right))
193f14f4646Smrg
194f14f4646Smrg    ;; if at bol and should indent only when starting a line
195f14f4646Smrg    (and (gethash :only-newline-indent options)
196f14f4646Smrg      (return-from python-should-indent (= point start)))
197f14f4646Smrg
198f14f4646Smrg    ;; at the start of a line
199f14f4646Smrg    (and (= point start)
200f14f4646Smrg      (return-from python-should-indent (gethash :newline-indent options)))
201f14f4646Smrg
202f14f4646Smrg    ;; if first character
203f14f4646Smrg    (and (= point (1+ start))
204f14f4646Smrg      (return-from python-should-indent t))
205f14f4646Smrg
206f14f4646Smrg    (setq offset start)
207f14f4646Smrg    (while (and
208f14f4646Smrg	     (< offset end)
209f14f4646Smrg	     (member (char-after offset) indent-spaces))
210f14f4646Smrg      (incf offset))
211f14f4646Smrg
212f14f4646Smrg    ;; cursor is at first character in line, with possible spaces before it
213f14f4646Smrg    (return-from python-should-indent (or (= offset end) (= offset (1- point))))
214f14f4646Smrg  )
215f14f4646Smrg  ;; Should not indent
216f14f4646Smrg  nil)
217f14f4646Smrg
218f14f4646Smrg(compile 'python-should-indent)
219f14f4646Smrg
220f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
221f14f4646Smrg(defun python-indent (syntax syntable)
222f14f4646Smrg  (let*
223f14f4646Smrg    ((options (syntax-options syntax))
224f14f4646Smrg      *base-indent*)
225f14f4646Smrg
226f14f4646Smrg    (or (python-should-indent options) (return-from python-indent))
227f14f4646Smrg    (setq
228f14f4646Smrg      *base-indent* (gethash :indentation options 4))
229f14f4646Smrg
230f14f4646Smrg    (indent-macro
231f14f4646Smrg      *python-mode-indent*
232f14f4646Smrg      (python-offset-indent)
233f14f4646Smrg      (gethash :emulate-tabs options))))
234f14f4646Smrg
235f14f4646Smrg(compile 'python-indent)
236f14f4646Smrg
237f14f4646Smrg
238f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
239f14f4646Smrg(defvar *python-mode-options* *python-DEFAULT-options*)
240f14f4646Smrg
241f14f4646Smrg
242f14f4646Smrg;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
243f14f4646Smrg(defsyntax *python-mode* :main nil #'python-indent *python-mode-options*
244f14f4646Smrg  ;; keywords
245f14f4646Smrg  (syntoken
246f14f4646Smrg    (string-concat
247f14f4646Smrg      "\\<("
248f14f4646Smrg      "and|break|class|continue|def|del|enumerate|except|False|for|"
249f14f4646Smrg      "elif|else|if|in|is|len|None|not|or|pass|print|raise|range|"
250f14f4646Smrg      "return|self|True|try|type|while|yield"
251f14f4646Smrg      ")\\>")
252f14f4646Smrg    :property *prop-keyword*)
253f14f4646Smrg
254f14f4646Smrg  (syntoken "^\\s+" :property *prop-indent*)
255f14f4646Smrg
256f14f4646Smrg  ;; preprocessor like
257f14f4646Smrg  (syntoken
258f14f4646Smrg    (string-concat
259f14f4646Smrg      "\\<("
260f14f4646Smrg      "from|import"
261f14f4646Smrg      ")\\>")
262f14f4646Smrg    :property *prop-preprocessor*)
263f14f4646Smrg
264f14f4646Smrg  ;; namespaces/accessors
265f14f4646Smrg  (syntoken "(\\w+\\.)+" :property *prop-preprocessor*)
266f14f4646Smrg
267f14f4646Smrg  ;; more preprocessor like
268f14f4646Smrg  (syntoken "\\<__[a-zA-Z0-9]+__\\>" :property *prop-keyword*)
269f14f4646Smrg
270f14f4646Smrg  ;; numbers
271f14f4646Smrg  (syntoken
272f14f4646Smrg    (string-concat
273f14f4646Smrg      "\\<("
274f14f4646Smrg      ;; Integers
275f14f4646Smrg      "(\\d+|0x\\x+)L?|"
276f14f4646Smrg      ;; Floats
277f14f4646Smrg      "\\d+\\.?\\d*(e[+-]?\\d+)?"
278f14f4646Smrg      ")\\>")
279f14f4646Smrg    :icase t
280f14f4646Smrg    :property *prop-number*)
281f14f4646Smrg
282f14f4646Smrg  ;; comments
283f14f4646Smrg  (syntoken "#.*" :property *prop-comment*)
284f14f4646Smrg
285f14f4646Smrg  ;; punctuation
286f14f4646Smrg  (syntoken "[][(){}+*/%^&<>=.,|!~:-]+" :property *prop-punctuation*)
287f14f4646Smrg
288f14f4646Smrg  ;; constant or constant like
289f14f4646Smrg  (syntoken "'" :nospec t :property *prop-constant* :begin :constant)
290f14f4646Smrg  (syntoken "'''" :nospec t :property *prop-constant* :begin :constant3)
291f14f4646Smrg
292f14f4646Smrg  ;; strings
293f14f4646Smrg  (syntoken "\"" :nospec t :property *prop-string* :begin :string)
294f14f4646Smrg  (syntoken "\"\"\"" :nospec t :property *prop-string* :begin :string3)
295f14f4646Smrg
296f14f4646Smrg  (syntable :constant *prop-constant* nil
297f14f4646Smrg    (syntoken "\\\\.")
298f14f4646Smrg    (syntoken "'" :nospec t :switch -1))
299f14f4646Smrg  (syntable :constant3 *prop-constant* nil
300f14f4646Smrg    (syntoken "'''" :nospec t :switch -1))
301f14f4646Smrg  (syntable :string *prop-string* nil
302f14f4646Smrg    (syntoken "\\\\.")
303f14f4646Smrg    (syntoken "\"" :nospec t :switch -1))
304f14f4646Smrg  (syntable :string3 *prop-string* nil
305f14f4646Smrg    (syntoken "\"\"\"" :nospec t :switch -1))
306f14f4646Smrg)
307