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