Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

305 lines
12 KiB

  1. ;;; cython-mode.el --- Major mode for editing Cython files
  2. ;; Package-Version: 0.29.23
  3. ;; Package-Commit: 17670781083e3ccfedb1af4adcec614d4599eef9
  4. ;; License: Apache-2.0
  5. ;;; Commentary:
  6. ;; This should work with python-mode.el as well as either the new
  7. ;; python.el or the old.
  8. ;;; Code:
  9. ;; Load python-mode if available, otherwise use builtin emacs python package
  10. (when (not (require 'python-mode nil t))
  11. (require 'python))
  12. (eval-when-compile (require 'rx))
  13. ;;;###autoload
  14. (add-to-list 'auto-mode-alist '("\\.pyx\\'" . cython-mode))
  15. ;;;###autoload
  16. (add-to-list 'auto-mode-alist '("\\.pxd\\'" . cython-mode))
  17. ;;;###autoload
  18. (add-to-list 'auto-mode-alist '("\\.pxi\\'" . cython-mode))
  19. (defvar cython-buffer nil
  20. "Variable pointing to the cython buffer which was compiled.")
  21. (defun cython-compile ()
  22. "Compile the file via Cython."
  23. (interactive)
  24. (let ((cy-buffer (current-buffer)))
  25. (with-current-buffer
  26. (compile compile-command)
  27. (set (make-local-variable 'cython-buffer) cy-buffer)
  28. (add-to-list (make-local-variable 'compilation-finish-functions)
  29. 'cython-compilation-finish))))
  30. (defun cython-compilation-finish (buffer how)
  31. "Called when Cython compilation finishes."
  32. ;; XXX could annotate source here
  33. )
  34. (defvar cython-mode-map
  35. (let ((map (make-sparse-keymap)))
  36. ;; Will inherit from `python-mode-map' thanks to define-derived-mode.
  37. (define-key map "\C-c\C-c" 'cython-compile)
  38. map)
  39. "Keymap used in `cython-mode'.")
  40. (defvar cython-font-lock-keywords
  41. `(;; ctypedef statement: "ctypedef (...type... alias)?"
  42. (,(rx
  43. ;; keyword itself
  44. symbol-start (group "ctypedef")
  45. ;; type specifier: at least 1 non-identifier symbol + 1 identifier
  46. ;; symbol and anything but a comment-starter after that.
  47. (opt (regexp "[^a-zA-Z0-9_\n]+[a-zA-Z0-9_][^#\n]*")
  48. ;; type alias: an identifier
  49. symbol-start (group (regexp "[a-zA-Z_]+[a-zA-Z0-9_]*"))
  50. ;; space-or-comments till the end of the line
  51. (* space) (opt "#" (* nonl)) line-end))
  52. (1 font-lock-keyword-face)
  53. (2 font-lock-type-face nil 'noerror))
  54. ;; new keywords in Cython language
  55. (,(rx symbol-start
  56. (or "by" "cdef" "cimport" "cpdef"
  57. "extern" "gil" "include" "nogil" "property" "public"
  58. "readonly" "DEF" "IF" "ELIF" "ELSE"
  59. "new" "del" "cppclass" "namespace" "const"
  60. "__stdcall" "__cdecl" "__fastcall" "inline" "api")
  61. symbol-end)
  62. . font-lock-keyword-face)
  63. ;; Question mark won't match at a symbol-end, so 'except?' must be
  64. ;; special-cased. It's simpler to handle it separately than weaving it
  65. ;; into the lengthy list of other keywords.
  66. (,(rx symbol-start "except?") . font-lock-keyword-face)
  67. ;; C and Python types (highlight as builtins)
  68. (,(rx symbol-start
  69. (or
  70. "object" "dict" "list"
  71. ;; basic c type names
  72. "void" "char" "int" "float" "double" "bint"
  73. ;; longness/signed/constness
  74. "signed" "unsigned" "long" "short"
  75. ;; special basic c types
  76. "size_t" "Py_ssize_t" "Py_UNICODE" "Py_UCS4" "ssize_t" "ptrdiff_t")
  77. symbol-end)
  78. . font-lock-builtin-face)
  79. (,(rx symbol-start "NULL" symbol-end)
  80. . font-lock-constant-face)
  81. ;; cdef is used for more than functions, so simply highlighting the next
  82. ;; word is problematic. struct, enum and property work though.
  83. (,(rx symbol-start
  84. (group (or "struct" "enum" "union"
  85. (seq "ctypedef" (+ space "fused"))))
  86. (+ space) (group (regexp "[a-zA-Z_]+[a-zA-Z0-9_]*")))
  87. (1 font-lock-keyword-face prepend) (2 font-lock-type-face))
  88. ("\\_<property[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
  89. 1 font-lock-function-name-face))
  90. "Additional font lock keywords for Cython mode.")
  91. ;;;###autoload
  92. (defgroup cython nil "Major mode for editing and compiling Cython files"
  93. :group 'languages
  94. :prefix "cython-"
  95. :link '(url-link :tag "Homepage" "http://cython.org"))
  96. ;;;###autoload
  97. (defcustom cython-default-compile-format "cython -a %s"
  98. "Format for the default command to compile a Cython file.
  99. It will be passed to `format' with `buffer-file-name' as the only other argument."
  100. :group 'cython
  101. :type 'string)
  102. ;; Some functions defined differently in the different python modes
  103. (defun cython-comment-line-p ()
  104. "Return non-nil if current line is a comment."
  105. (save-excursion
  106. (back-to-indentation)
  107. (eq ?# (char-after (point)))))
  108. (defun cython-in-string/comment ()
  109. "Return non-nil if point is in a comment or string."
  110. (nth 8 (syntax-ppss)))
  111. (defalias 'cython-beginning-of-statement
  112. (cond
  113. ;; python-mode.el
  114. ((fboundp 'py-beginning-of-statement)
  115. 'py-beginning-of-statement)
  116. ;; old python.el
  117. ((fboundp 'python-beginning-of-statement)
  118. 'python-beginning-of-statement)
  119. ;; new python.el
  120. ((fboundp 'python-nav-beginning-of-statement)
  121. 'python-nav-beginning-of-statement)
  122. (t (error "Couldn't find implementation for `cython-beginning-of-statement'"))))
  123. (defalias 'cython-beginning-of-block
  124. (cond
  125. ;; python-mode.el
  126. ((fboundp 'py-beginning-of-block)
  127. 'py-beginning-of-block)
  128. ;; old python.el
  129. ((fboundp 'python-beginning-of-block)
  130. 'python-beginning-of-block)
  131. ;; new python.el
  132. ((fboundp 'python-nav-beginning-of-block)
  133. 'python-nav-beginning-of-block)
  134. (t (error "Couldn't find implementation for `cython-beginning-of-block'"))))
  135. (defalias 'cython-end-of-statement
  136. (cond
  137. ;; python-mode.el
  138. ((fboundp 'py-end-of-statement)
  139. 'py-end-of-statement)
  140. ;; old python.el
  141. ((fboundp 'python-end-of-statement)
  142. 'python-end-of-statement)
  143. ;; new python.el
  144. ((fboundp 'python-nav-end-of-statement)
  145. 'python-nav-end-of-statement)
  146. (t (error "Couldn't find implementation for `cython-end-of-statement'"))))
  147. (defun cython-open-block-statement-p (&optional bos)
  148. "Return non-nil if statement at point opens a Cython block.
  149. BOS non-nil means point is known to be at beginning of statement."
  150. (save-excursion
  151. (unless bos (cython-beginning-of-statement))
  152. (looking-at (rx (and (or "if" "else" "elif" "while" "for" "def" "cdef" "cpdef"
  153. "class" "try" "except" "finally" "with"
  154. "EXAMPLES:" "TESTS:" "INPUT:" "OUTPUT:")
  155. symbol-end)))))
  156. (defun cython-beginning-of-defun ()
  157. "`beginning-of-defun-function' for Cython.
  158. Finds beginning of innermost nested class or method definition.
  159. Returns the name of the definition found at the end, or nil if
  160. reached start of buffer."
  161. (let ((ci (current-indentation))
  162. (def-re (rx line-start (0+ space) (or "def" "cdef" "cpdef" "class") (1+ space)
  163. (group (1+ (or word (syntax symbol))))))
  164. found lep) ;; def-line
  165. (if (cython-comment-line-p)
  166. (setq ci most-positive-fixnum))
  167. (while (and (not (bobp)) (not found))
  168. ;; Treat bol at beginning of function as outside function so
  169. ;; that successive C-M-a makes progress backwards.
  170. ;;(setq def-line (looking-at def-re))
  171. (unless (bolp) (end-of-line))
  172. (setq lep (line-end-position))
  173. (if (and (re-search-backward def-re nil 'move)
  174. ;; Must be less indented or matching top level, or
  175. ;; equally indented if we started on a definition line.
  176. (let ((in (current-indentation)))
  177. (or (and (zerop ci) (zerop in))
  178. (= lep (line-end-position)) ; on initial line
  179. ;; Not sure why it was like this -- fails in case of
  180. ;; last internal function followed by first
  181. ;; non-def statement of the main body.
  182. ;;(and def-line (= in ci))
  183. (= in ci)
  184. (< in ci)))
  185. (not (cython-in-string/comment)))
  186. (setq found t)))))
  187. (defun cython-end-of-defun ()
  188. "`end-of-defun-function' for Cython.
  189. Finds end of innermost nested class or method definition."
  190. (let ((orig (point))
  191. (pattern (rx line-start (0+ space) (or "def" "cdef" "cpdef" "class") space)))
  192. ;; Go to start of current block and check whether it's at top
  193. ;; level. If it is, and not a block start, look forward for
  194. ;; definition statement.
  195. (when (cython-comment-line-p)
  196. (end-of-line)
  197. (forward-comment most-positive-fixnum))
  198. (when (not (cython-open-block-statement-p))
  199. (cython-beginning-of-block))
  200. (if (zerop (current-indentation))
  201. (unless (cython-open-block-statement-p)
  202. (while (and (re-search-forward pattern nil 'move)
  203. (cython-in-string/comment))) ; just loop
  204. (unless (eobp)
  205. (beginning-of-line)))
  206. ;; Don't move before top-level statement that would end defun.
  207. (end-of-line)
  208. (beginning-of-defun))
  209. ;; If we got to the start of buffer, look forward for
  210. ;; definition statement.
  211. (when (and (bobp) (not (looking-at (rx (or "def" "cdef" "cpdef" "class")))))
  212. (while (and (not (eobp))
  213. (re-search-forward pattern nil 'move)
  214. (cython-in-string/comment)))) ; just loop
  215. ;; We're at a definition statement (or end-of-buffer).
  216. ;; This is where we should have started when called from end-of-defun
  217. (unless (eobp)
  218. (let ((block-indentation (current-indentation)))
  219. (python-nav-end-of-statement)
  220. (while (and (forward-line 1)
  221. (not (eobp))
  222. (or (and (> (current-indentation) block-indentation)
  223. (or (cython-end-of-statement) t))
  224. ;; comment or empty line
  225. (looking-at (rx (0+ space) (or eol "#"))))))
  226. (forward-comment -1))
  227. ;; Count trailing space in defun (but not trailing comments).
  228. (skip-syntax-forward " >")
  229. (unless (eobp) ; e.g. missing final newline
  230. (beginning-of-line)))
  231. ;; Catch pathological cases like this, where the beginning-of-defun
  232. ;; skips to a definition we're not in:
  233. ;; if ...:
  234. ;; ...
  235. ;; else:
  236. ;; ... # point here
  237. ;; ...
  238. ;; def ...
  239. (if (< (point) orig)
  240. (goto-char (point-max)))))
  241. (defun cython-current-defun ()
  242. "`add-log-current-defun-function' for Cython."
  243. (save-excursion
  244. ;; Move up the tree of nested `class' and `def' blocks until we
  245. ;; get to zero indentation, accumulating the defined names.
  246. (let ((start t)
  247. accum)
  248. (while (or start (> (current-indentation) 0))
  249. (setq start nil)
  250. (cython-beginning-of-block)
  251. (end-of-line)
  252. (beginning-of-defun)
  253. (if (looking-at (rx (0+ space) (or "def" "cdef" "cpdef" "class") (1+ space)
  254. (group (1+ (or word (syntax symbol))))))
  255. (push (match-string 1) accum)))
  256. (if accum (mapconcat 'identity accum ".")))))
  257. ;;;###autoload
  258. (define-derived-mode cython-mode python-mode "Cython"
  259. "Major mode for Cython development, derived from Python mode.
  260. \\{cython-mode-map}"
  261. (font-lock-add-keywords nil cython-font-lock-keywords)
  262. (set (make-local-variable 'outline-regexp)
  263. (rx (* space) (or "class" "def" "cdef" "cpdef" "elif" "else" "except" "finally"
  264. "for" "if" "try" "while" "with")
  265. symbol-end))
  266. (set (make-local-variable 'beginning-of-defun-function)
  267. #'cython-beginning-of-defun)
  268. (set (make-local-variable 'end-of-defun-function)
  269. #'cython-end-of-defun)
  270. (set (make-local-variable 'compile-command)
  271. (format cython-default-compile-format (shell-quote-argument (or buffer-file-name ""))))
  272. (set (make-local-variable 'add-log-current-defun-function)
  273. #'cython-current-defun)
  274. (add-hook 'which-func-functions #'cython-current-defun nil t)
  275. (add-to-list (make-local-variable 'compilation-finish-functions)
  276. 'cython-compilation-finish))
  277. (provide 'cython-mode)
  278. ;;; cython-mode.el ends here