Emacs: Altering python.el indentation in derived mode

239 views Asked by At

I am attempting to derive a new emacs mode from python.el (the current official gnu one) for Boo and I am having trouble with altering the indentation. Does anyone have any suggestions about how to best handle this? I do not need to change anything drastically, just add some new block forms and stuff.

For example, since this is for Boo, the try/except syntax uses "ensure" instead of "finally". I can change this easily enough in python.el by changing the block-start def of python-rx-constituents. However, I can't seem to be able to override this in a derived mode because python-rx-constituents is being then used by a macro, python-rx, and I guess once those two things are defined when python.el loads (as it has to, since I am deriving from it), I can no longer override it after-load or in a hook? Because I've definitely changed it in memory and in a hook after python.el loads and in an after-load statement and none of them work. While directly altering python.el works fine.

Here is the code in question from python.el:

(eval-when-compile
  (defconst python-rx-constituents
    `((block-start          . ,(rx symbol-start
                                   (or "def" "class" "if" "elif" "else" "try"
                                       "except" "finally" "for" "while" "with"
                                       )
                                   symbol-end))
      (decorator            . ,(rx line-start (* space) ?@ (any letter ?_)
                                   (* (any word ?_))))
      (defun                . ,(rx symbol-start (or "def" "class") symbol-end))
      (if-name-main         . ,(rx line-start "if" (+ space) "__name__"
                                   (+ space) "==" (+ space)
                                   (any ?' ?\") "__main__" (any ?' ?\")
                                   (* space) ?:))
      (symbol-name          . ,(rx (any letter ?_) (* (any word ?_))))
      (open-paren           . ,(rx (or "{" "[" "(")))
      (close-paren          . ,(rx (or "}" "]" ")")))
      (simple-operator      . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)))
      ;; FIXME: rx should support (not simple-operator).
      (not-simple-operator  . ,(rx
                                (not
                                 (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%))))
      ;; FIXME: Use regexp-opt.
      (operator             . ,(rx (or "+" "-" "/" "&" "^" "~" "|" "*" "<" ">"
                                       "=" "%" "**" "//" "<<" ">>" "<=" "!="
                                       "==" ">=" "is" "not")))
      ;; FIXME: Use regexp-opt.
      (assignment-operator  . ,(rx (or "=" "+=" "-=" "*=" "/=" "//=" "%=" "**="
                                       ">>=" "<<=" "&=" "^=" "|=")))
      (string-delimiter . ,(rx (and
                                ;; Match even number of backslashes.
                                (or (not (any ?\\ ?\' ?\")) point
                                    ;; Quotes might be preceded by a escaped quote.
                                    (and (or (not (any ?\\)) point) ?\\
                                         (* ?\\ ?\\) (any ?\' ?\")))
                                (* ?\\ ?\\)
                                ;; Match single or triple quotes of any kind.
                                (group (or  "\"" "\"\"\"" "'" "'''"))))))
    "Additional Python specific sexps for `python-rx'")

  (defmacro python-rx (&rest regexps)
    "Python mode specialized rx macro.
This variant of `rx' supports common python named REGEXPS."
    (let ((rx-constituents (append python-rx-constituents rx-constituents)))
      (cond ((null regexps)
             (error "No regexp"))
            ((cdr regexps)
             (rx-to-string `(and ,@regexps) t))
            (t
             (rx-to-string (car regexps) t))))))

I would like to change python-rx-constituents so that block-start includes "ensure" instead of finally.

2

There are 2 answers

0
Andreas Röhler On

As commented already, employing derived-mode is not suitable here: you can't back-change a macro. Also re-defining it isn't recommendable: the order of loading/evaluation than will decide which one is in effect - at a larger scale that means running into a mess.

Copy the file, store as boo.el, replace the prefix by "boo-", reload and edit the stuff which needs changing.

Your concern expressed IMO isn't justified, as permitting copying, changing and re-release of the changed code is the core of the GPL.

0
Stefan On

Copying the file is a bad idea, indeed, since it makes it painful to track the evolution of python.el. Deriving is not that great an idea either, since Boo isn't really an extension or a variation of Python, so you probably want to reuse some of its indentation machinery and some of its code to understand the significant-indentation, but not much more than that.

You might like to contact python.el's author (with Cc to emacs-devel) to see if python.el could be adjusted to make your life easier. e.g. maybe extract the common code to an auxiliary file that can be shared between the two. Ideally, this file might be usable for cofferscript-mode and maybe Haskell-mode as well.