diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | lisp/modules/progmodes/python.lsp | 306 | ||||
-rw-r--r-- | lisp/modules/xedit.lsp | 2 |
3 files changed, 309 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 39d8b0b..7d70784 100644 --- a/Makefile.am +++ b/Makefile.am @@ -201,6 +201,7 @@ dist_progmodes_DATA = \ ${srcdir}/lisp/modules/progmodes/man.lsp \ ${srcdir}/lisp/modules/progmodes/patch.lsp \ ${srcdir}/lisp/modules/progmodes/perl.lsp \ + ${srcdir}/lisp/modules/progmodes/python.lsp \ ${srcdir}/lisp/modules/progmodes/rpm.lsp \ ${srcdir}/lisp/modules/progmodes/sgml.lsp \ ${srcdir}/lisp/modules/progmodes/sh.lsp \ diff --git a/lisp/modules/progmodes/python.lsp b/lisp/modules/progmodes/python.lsp new file mode 100644 index 0000000..ff70856 --- /dev/null +++ b/lisp/modules/progmodes/python.lsp @@ -0,0 +1,306 @@ +;; Copyright (c) 2008 Paulo Cesar Pereira de Andrade +;; +;; Permission is hereby granted, free of charge, to any person obtaining a +;; copy of this software and associated documentation files (the "Software"), +;; to deal in the Software without restriction, including without limitation +;; the rights to use, copy, modify, merge, publish, distribute, sublicense, +;; and/or sell copies of the Software, and to permit persons to whom the +;; Software is furnished to do so, subject to the following conditions: +;; +;; The above copyright notice and this permission notice (including the next +;; paragraph) shall be included in all copies or substantial portions of the +;; Software. +;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;; DEALINGS IN THE SOFTWARE. +;; +;; Author: Paulo Cesar Pereira de Andrade +;; + +(require "syntax") +(require "indent") +(in-package "XEDIT") + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defsynprop *prop-indent* + "indent" + :font "*courier-medium-r*-12-*" + :background "Gray92") + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defsynoptions *python-DEFAULT-options* + ;; Positive number. Basic indentation + (:indentation . 4) + + ;; Boolean. Move cursor to the indent column after pressing <Enter>? + (:newline-indent . t) + + ;; Boolean. Set to T if tabs shouldn't be used to fill indentation. + (:emulate-tabs . t) + + ;; Boolean. Only calculate indentation after pressing <Enter>? + ;; This may be useful if the parser does not always + ;; do what the user expects... + (:only-newline-indent . nil) + + ;; Boolean. Remove extra spaces from previous line. + ;; This should default to T when newline-indent is not NIL. + (:trim-blank-lines . nil) + + ;; Boolean. If this hash-table entry is set, no indentation is done. + ;; Useful to temporarily disable indentation. + (:disable-indent . nil)) + + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +;; Not doing "special" indentation of multiline ( because it is attempting +;; to do a "smart" indentation and usually don't read more then one line +;; back to resolve indentation. +;; Code for multiline { and [, usually declaring vector/hash like variables +;; should be working properly. +;; Note that the indent lisp hook is only run on character additions, so +;; it doesn't do a "smart" tabbing when pressing backspace, but it will +;; properly align to the "closest tab stop" when typping a character. +(defindent *python-mode-indent* :main + ;; this must be the first token + (indtoken "^\\s*" :indent + :code (or *offset* (setq *offset* (+ *ind-offset* *ind-length*)))) + + ;; ignore comments + (indtoken "#.*$" nil) + + (indtoken ":" :collon :nospec t) + + ;; don't directly match {}, [], () strings, and : + (indtoken "[a-zA-Z0-9+*/%^&<>=.,|!~-]+" :expression) + + ;; if in the same line, reduce now, as delimiters are identical + (indtoken "'([^\\']|\\\\.)*'" :expression) + (indtoken "\"([^\\\"]|\\\\.)*\"" :expression) + ;; otherwise, use a table + (indtoken "\"" :cstring :nospec t :begin :string) + (indtoken "'" :cconstant :nospec t :begin :constant) + (indtoken "\"\"\"" :cstring3 :nospec t :begin :string3) + (indtoken "'''" :cconstant :nospec t :begin :constant3) + + (indinit (braces 0)) + (indtoken "}" :cbrace :nospec t :code (incf braces)) + (indtoken "{" :obrace :nospec t :code (decf braces)) + (indtoken ")" :cparen :nospec t :code (incf braces)) + (indtoken "(" :oparen :nospec t :code (decf braces)) + (indtoken "]" :cbrack :nospec t :code (incf braces)) + (indtoken "[" :obrack :nospec t :code (decf braces)) + + ;; This must be the last token + (indtoken "$" :eol) + + (indtable :string + ;; Ignore escaped characters + (indtoken "\\." nil) + ;; Return to the toplevel when the start of the string is found + (indtoken "\"" :ostring :nospec t :switch -1)) + (indtable :constant + (indtoken "\\." nil) + (indtoken "'" :oconstant :nospec t :switch -1)) + + (indtable :string3 + (indtoken "\"\"\"" :ostring3 :nospec t :switch -1)) + (indtable :constant3 + (indtoken "'''" :oconstant3 :nospec t :switch -1)) + + ;; Reduce what isn't reduced in regex pattern match + (indreduce :expression + t + ((:expression :expression) + ;; multiline strings + (:ostring (not :ostring) :cstring) + (:oconstant (not :oconstant) :cconstant) + (:ostring3 (not :ostring3) :cstring3) + (:oconstant3 (not :oconstant3) :cconstant3) + ;; braces, parenthesis and brackets + (:obrace (not :obrace) :cbrace) + (:oparen (not :oparen) :cparen) + (:obrack (not :obrack) :cbrack))) + + ;; This should be the most common exit point; + ;; just copy previous line indentation. + (indreduce :align + (< *ind-offset* *ind-start*) + ((:indent :eol) + (:indent :expression :eol)) + (setq *indent* (offset-indentation *offset* :resolve t)) + + ;; If cursor is not in an indentation tab, assume user is trying to align + ;; to another block, and just use the resolve code to round it down + (unless (/= (mod *indent* *base-indent*) 0) + ;; else use "previous-line" indentation. + (setq *indent* (offset-indentation *ind-offset* :resolve t))) + (indent-macro-reject-left)) + + ;; This should be second most common exit point; + ;; add one indentation level. + (indreduce :align + (< *ind-offset* *ind-start*) + ((:indent :expression :collon :eol)) + (setq *indent* (+ *base-indent* (offset-indentation *ind-offset* :resolve t))) + (indent-macro-reject-left)) + + (indresolve :align + (setq *indent* (- *indent* (mod *indent* *base-indent*)))) + + ;; Calculate special indentation for [ and { + (indresolve (:obrack :obrace) + (and + (< *ind-offset* *ind-start*) + (setq *indent* (+ *base-indent* + (offset-indentation *ind-offset* :resolve t))))) + (indresolve (:cbrack :cbrace) + (setq *indent* (- (offset-indentation *ind-offset* :resolve t) + (if (>= *ind-offset* *ind-start*) + *base-indent* 0)))) +) + + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defun python-offset-indent (&aux char (point (point))) + ;; Skip spaces forward + (while (member (setq char (char-after point)) indent-spaces) + (incf point)) + point) + +(compile 'python-offset-indent) + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defun python-should-indent (options &aux point start end offset) + (when (hash-table-p options) + ;; check if previous line has extra spaces + (and (gethash :trim-blank-lines options) + (indent-clear-empty-line)) + + ;; indentation disabled? + (and (gethash :disable-indent options) + (return-from python-should-indent)) + + (setq + point (point) + start (scan point :eol :left) + end (scan point :eol :right)) + + ;; if at bol and should indent only when starting a line + (and (gethash :only-newline-indent options) + (return-from python-should-indent (= point start))) + + ;; at the start of a line + (and (= point start) + (return-from python-should-indent (gethash :newline-indent options))) + + ;; if first character + (and (= point (1+ start)) + (return-from python-should-indent t)) + + (setq offset start) + (while (and + (< offset end) + (member (char-after offset) indent-spaces)) + (incf offset)) + + ;; cursor is at first character in line, with possible spaces before it + (return-from python-should-indent (or (= offset end) (= offset (1- point)))) + ) + ;; Should not indent + nil) + +(compile 'python-should-indent) + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defun python-indent (syntax syntable) + (let* + ((options (syntax-options syntax)) + *base-indent*) + + (or (python-should-indent options) (return-from python-indent)) + (setq + *base-indent* (gethash :indentation options 4)) + + (indent-macro + *python-mode-indent* + (python-offset-indent) + (gethash :emulate-tabs options)))) + +(compile 'python-indent) + + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defvar *python-mode-options* *python-DEFAULT-options*) + + +;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +(defsyntax *python-mode* :main nil #'python-indent *python-mode-options* + ;; keywords + (syntoken + (string-concat + "\\<(" + "and|break|class|continue|def|del|enumerate|except|False|for|" + "elif|else|if|in|is|len|None|not|or|pass|print|raise|range|" + "return|self|True|try|type|while|yield" + ")\\>") + :property *prop-keyword*) + + (syntoken "^\\s+" :property *prop-indent*) + + ;; preprocessor like + (syntoken + (string-concat + "\\<(" + "from|import" + ")\\>") + :property *prop-preprocessor*) + + ;; namespaces/accessors + (syntoken "(\\w+\\.)+" :property *prop-preprocessor*) + + ;; more preprocessor like + (syntoken "\\<__[a-zA-Z0-9]+__\\>" :property *prop-keyword*) + + ;; numbers + (syntoken + (string-concat + "\\<(" + ;; Integers + "(\\d+|0x\\x+)L?|" + ;; Floats + "\\d+\\.?\\d*(e[+-]?\\d+)?" + ")\\>") + :icase t + :property *prop-number*) + + ;; comments + (syntoken "#.*" :property *prop-comment*) + + ;; punctuation + (syntoken "[][(){}+*/%^&<>=.,|!~:-]+" :property *prop-punctuation*) + + ;; constant or constant like + (syntoken "'" :nospec t :property *prop-constant* :begin :constant) + (syntoken "'''" :nospec t :property *prop-constant* :begin :constant3) + + ;; strings + (syntoken "\"" :nospec t :property *prop-string* :begin :string) + (syntoken "\"\"\"" :nospec t :property *prop-string* :begin :string3) + + (syntable :constant *prop-constant* nil + (syntoken "\\\\.") + (syntoken "'" :nospec t :switch -1)) + (syntable :constant3 *prop-constant* nil + (syntoken "'''" :nospec t :switch -1)) + (syntable :string *prop-string* nil + (syntoken "\\\\.") + (syntoken "\"" :nospec t :switch -1)) + (syntable :string3 *prop-string* nil + (syntoken "\"\"\"" :nospec t :switch -1)) +) diff --git a/lisp/modules/xedit.lsp b/lisp/modules/xedit.lsp index d3e2a02..2b76970 100644 --- a/lisp/modules/xedit.lsp +++ b/lisp/modules/xedit.lsp @@ -70,6 +70,8 @@ "RPM spec" "rpm" . *rpm-mode*) ("\\.(pl|pm|ph)$" "Perl" "perl" . *perl-mode*) + ("\\.(py)$" + "Python" "python". *python-mode*) ("\\.(sgml?|dtd)$" "SGML" "sgml" . *sgml-mode*) ("\\.html?$" |