:group 'cython
:type 'string)
+;; Some functions defined differently in the different python modes
+(defun cython-comment-line-p ()
+ "Return non-nil if current line is a comment."
+ (save-excursion
+ (back-to-indentation)
+ (eq ?# (char-after (point)))))
+
+(defun cython-in-string/comment ()
+ "Return non-nil if point is in a comment or string."
+ (nth 8 (syntax-ppss)))
+
+(defalias 'cython-beginning-of-statement
+ (cond
+ ;; python-mode.el
+ ((fboundp 'py-beginning-of-statement)
+ 'py-beginning-of-statement)
+ ;; old python.el
+ ((fboundp 'python-beginning-of-statement)
+ 'python-beginning-of-statement)
+ ;; new python.el
+ ((fboundp 'python-nav-beginning-of-statement)
+ 'python-nav-beginning-of-statement)
+ (t (error "Couldn't find implementation for `cython-beginning-of-statement'"))))
+
+(defalias 'cython-beginning-of-block
+ (cond
+ ;; python-mode.el
+ ((fboundp 'py-beginning-of-block)
+ 'py-beginning-of-block)
+ ;; old python.el
+ ((fboundp 'python-beginning-of-block)
+ 'python-beginning-of-block)
+ ;; new python.el
+ ((fboundp 'python-nav-beginning-of-block)
+ 'python-nav-beginning-of-block)
+ (t (error "Couldn't find implementation for `cython-beginning-of-block'"))))
+
+(defalias 'cython-end-of-statement
+ (cond
+ ;; python-mode.el
+ ((fboundp 'py-end-of-statement)
+ 'py-end-of-statement)
+ ;; old python.el
+ ((fboundp 'python-end-of-statement)
+ 'python-end-of-statement)
+ ;; new python.el
+ ((fboundp 'python-nav-end-of-statement)
+ 'python-nav-end-of-statement)
+ (t (error "Couldn't find implementation for `cython-end-of-statement'"))))
+
+(defun cython-open-block-statement-p (&optional bos)
+ "Return non-nil if statement at point opens a Cython block.
+BOS non-nil means point is known to be at beginning of statement."
+ (save-excursion
+ (unless bos (cython-beginning-of-statement))
+ (looking-at (rx (and (or "if" "else" "elif" "while" "for" "def" "cdef" "cpdef"
+ "class" "try" "except" "finally" "with"
+ "EXAMPLES:" "TESTS:" "INPUT:" "OUTPUT:")
+ symbol-end)))))
+
+(defun cython-beginning-of-defun ()
+ "`beginning-of-defun-function' for Cython.
+Finds beginning of innermost nested class or method definition.
+Returns the name of the definition found at the end, or nil if
+reached start of buffer."
+ (let ((ci (current-indentation))
+ (def-re (rx line-start (0+ space) (or "def" "cdef" "cpdef" "class") (1+ space)
+ (group (1+ (or word (syntax symbol))))))
+ found lep) ;; def-line
+ (if (cython-comment-line-p)
+ (setq ci most-positive-fixnum))
+ (while (and (not (bobp)) (not found))
+ ;; Treat bol at beginning of function as outside function so
+ ;; that successive C-M-a makes progress backwards.
+ ;;(setq def-line (looking-at def-re))
+ (unless (bolp) (end-of-line))
+ (setq lep (line-end-position))
+ (if (and (re-search-backward def-re nil 'move)
+ ;; Must be less indented or matching top level, or
+ ;; equally indented if we started on a definition line.
+ (let ((in (current-indentation)))
+ (or (and (zerop ci) (zerop in))
+ (= lep (line-end-position)) ; on initial line
+ ;; Not sure why it was like this -- fails in case of
+ ;; last internal function followed by first
+ ;; non-def statement of the main body.
+ ;;(and def-line (= in ci))
+ (= in ci)
+ (< in ci)))
+ (not (cython-in-string/comment)))
+ (setq found t)))))
+
+(defun cython-end-of-defun ()
+ "`end-of-defun-function' for Cython.
+Finds end of innermost nested class or method definition."
+ (let ((orig (point))
+ (pattern (rx line-start (0+ space) (or "def" "cdef" "cpdef" "class") space)))
+ ;; Go to start of current block and check whether it's at top
+ ;; level. If it is, and not a block start, look forward for
+ ;; definition statement.
+ (when (cython-comment-line-p)
+ (end-of-line)
+ (forward-comment most-positive-fixnum))
+ (when (not (cython-open-block-statement-p))
+ (cython-beginning-of-block))
+ (if (zerop (current-indentation))
+ (unless (cython-open-block-statement-p)
+ (while (and (re-search-forward pattern nil 'move)
+ (cython-in-string/comment))) ; just loop
+ (unless (eobp)
+ (beginning-of-line)))
+ ;; Don't move before top-level statement that would end defun.
+ (end-of-line)
+ (beginning-of-defun))
+ ;; If we got to the start of buffer, look forward for
+ ;; definition statement.
+ (when (and (bobp) (not (looking-at (rx (or "def" "cdef" "cpdef" "class")))))
+ (while (and (not (eobp))
+ (re-search-forward pattern nil 'move)
+ (cython-in-string/comment)))) ; just loop
+ ;; We're at a definition statement (or end-of-buffer).
+ ;; This is where we should have started when called from end-of-defun
+ (unless (eobp)
+ (let ((block-indentation (current-indentation)))
+ (python-nav-end-of-statement)
+ (while (and (forward-line 1)
+ (not (eobp))
+ (or (and (> (current-indentation) block-indentation)
+ (or (cython-end-of-statement) t))
+ ;; comment or empty line
+ (looking-at (rx (0+ space) (or eol "#"))))))
+ (forward-comment -1))
+ ;; Count trailing space in defun (but not trailing comments).
+ (skip-syntax-forward " >")
+ (unless (eobp) ; e.g. missing final newline
+ (beginning-of-line)))
+ ;; Catch pathological cases like this, where the beginning-of-defun
+ ;; skips to a definition we're not in:
+ ;; if ...:
+ ;; ...
+ ;; else:
+ ;; ... # point here
+ ;; ...
+ ;; def ...
+ (if (< (point) orig)
+ (goto-char (point-max)))))
+
;;;###autoload
(define-derived-mode cython-mode python-mode "Cython"
"Major mode for Cython development, derived from Python mode.
(rx (* space) (or "class" "def" "cdef" "cpdef" "elif" "else" "except" "finally"
"for" "if" "try" "while" "with")
symbol-end))
+ (set (make-local-variable 'beginning-of-defun-function)
+ #'cython-beginning-of-defun)
+ (set (make-local-variable 'end-of-defun-function)
+ #'cython-end-of-defun)
(set (make-local-variable 'compile-command)
(format cython-default-compile-format (shell-quote-argument buffer-file-name)))
(add-to-list (make-local-variable 'compilation-finish-functions)