ein-mumamo.el 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. ;;; ein-mumamo.el --- MuMaMo for notebook
  2. ;; Copyright (C) 2012- Takafumi Arakaki
  3. ;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
  4. ;; This file is NOT part of GNU Emacs.
  5. ;; ein-mumamo.el is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; ein-mumamo.el is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with ein-mumamo.el. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;;; Code:
  18. (require 'mumamo)
  19. (require 'ein-worksheet)
  20. ;;; Customization
  21. (defcustom ein:mumamo-codecell-mode 'python-mode
  22. "Major Mode for Code Cell."
  23. :type '(symbol :tag "Major Mode")
  24. :group 'ein)
  25. (defcustom ein:mumamo-textcell-mode 'text-mode
  26. "Major Mode for Text Cell."
  27. :type '(symbol :tag "Major Mode")
  28. :group 'ein)
  29. (defcustom ein:mumamo-htmlcell-mode 'html-mode
  30. "Major Mode for HTML Cell."
  31. :type '(symbol :tag "Major Mode")
  32. :group 'ein)
  33. (defcustom ein:mumamo-markdowncell-mode 'markdown-mode
  34. "Major Mode for Markdown Cell."
  35. :type '(symbol :tag "Major Mode")
  36. :group 'ein)
  37. (defcustom ein:mumamo-rawcell-mode 'rst-mode
  38. "Major Mode for Raw Cell."
  39. :type '(symbol :tag "Major Mode")
  40. :group 'ein)
  41. (defcustom ein:mumamo-headingcell-mode 'text-mode
  42. "Major Mode for Heading Cell."
  43. :type '(symbol :tag "Major Mode")
  44. :group 'ein)
  45. (defcustom ein:mumamo-fallback-mode 'text-mode
  46. "Fallback Major Mode."
  47. :type '(symbol :tag "Major Mode")
  48. :group 'ein)
  49. (defcustom ein:use-mumamo-indent-line-function-workaround t
  50. "Turn on workaround for `mumamo-indent-line-function'.
  51. In code cell, hitting TAB or C-j at the end of input area causes
  52. error from MuMaMo. When this variable is non-`nil', EIN patches
  53. `mumamo-indent-line-function' to workaround this problem. This
  54. workaround is on by default.
  55. Note that python-mode's indentation function has other problems
  56. with MuMaMo. For example, hitting TAB twice, which decreases the
  57. indentation level by one in normal Python buffer, causes similar
  58. error in code cell. The current workaround does not fix this
  59. problem."
  60. :type 'boolean
  61. :group 'ein)
  62. (defcustom ein:mumamo-indent-line-function-dummy-code "
  63. def ein_dummy():
  64. return"
  65. "Dummy code block for `mumamo-indent-line-function' workaround.
  66. This code block will be inserted at the end of cell input before
  67. indentation and then removed afterward (so user will not see this
  68. code).
  69. This is ugly but... \"practicality beats purity\"...
  70. I guess somebody should fix python.el and/or MuMaMo, in order to
  71. remove this ugliness.
  72. To make the workaround less aggressive, you can set a newline
  73. \"\\n\" for this variable. In that case, you will be affected by
  74. `issue 24`_.
  75. .. _issue 24: https://github.com/tkf/emacs-ipython-notebook/issues/24"
  76. :type 'boolean
  77. :group 'ein)
  78. ;;; Workaround
  79. (defadvice mumamo-indent-line-function
  80. (around ein:mumamo-indent-line-function-workaround)
  81. "Workaround the indentation problem when the cursor is in the
  82. code cell."
  83. (let ((cell (ein:worksheet-get-current-cell)))
  84. ;; Check if the current buffer is notebook AND the current cell is
  85. ;; code cell.
  86. (if (ein:codecell-p cell)
  87. (let ((cur (copy-marker (point)))
  88. (end (copy-marker (1+ (ein:cell-input-pos-max cell)))))
  89. ;; v-- execute `delete-char' here
  90. ;; ... [] ......DUMMY
  91. ;; ^- cur ^- end (non-inclusive end of cell)
  92. ;; ^- `ad-do-it' here
  93. (unwind-protect
  94. (progn
  95. (goto-char (1- end))
  96. (insert ein:mumamo-indent-line-function-dummy-code)
  97. (goto-char cur)
  98. ad-do-it)
  99. (save-excursion
  100. (let ((len (length ein:mumamo-indent-line-function-dummy-code)))
  101. (goto-char (- end 1 len))
  102. (delete-char len)))))
  103. ad-do-it)))
  104. (defun ein:mumamo-indent-line-function-workaround-turn-on ()
  105. "Activate advice for `mumamo-indent-line-function'.
  106. Called via `ein:notebook-mumamo-mode-hook'."
  107. (when ein:use-mumamo-indent-line-function-workaround
  108. (ad-enable-advice 'mumamo-indent-line-function 'around
  109. 'ein:mumamo-indent-line-function-workaround)
  110. (ad-activate 'mumamo-indent-line-function)))
  111. (defun ein:mumamo-imenu-setup-maybe ()
  112. "Set `imenu-create-index-function' if the current buffer is the
  113. notebook buffer.
  114. This function is called via `after-change-major-mode-hook', to set
  115. the variable every time visiting the different chunks.
  116. .. note:: Making `imenu-create-index-function' permanent-local
  117. also solves the problem. However, this will make the variable
  118. permanent-local in *any* buffer, including the buffers
  119. irrelevant to EIN. Therefore, the current approach is taken.
  120. This is the same workaround as `ein:ac-setup-maybe'."
  121. (when (ein:worksheet-buffer-p)
  122. (ein:worksheet-imenu-setup)))
  123. (add-hook 'after-change-major-mode-hook 'ein:mumamo-imenu-setup-maybe)
  124. ;;; `ein:notebook-mumamo-mode'
  125. (define-derived-mode ein:notebook-bg-mode fundamental-mode "ein:bg"
  126. "Background mode for `ein:notebook-mumamo-mode'."
  127. (setq font-lock-defaults '(nil t))
  128. (font-lock-mode))
  129. (define-mumamo-multi-major-mode ein:notebook-mumamo-mode
  130. "IPython notebook mode."
  131. ("IPython notebook familiy" ein:notebook-bg-mode
  132. (ein:mumamo-chunk-codecell
  133. ein:mumamo-chunk-textcell
  134. ein:mumamo-chunk-htmlcell
  135. ein:mumamo-chunk-markdowncell
  136. ein:mumamo-chunk-rawcell
  137. ein:mumamo-chunk-headingcell
  138. )))
  139. (add-hook 'ein:notebook-mumamo-mode-hook
  140. 'ein:mumamo-indent-line-function-workaround-turn-on)
  141. ;;; Chunk functions
  142. (defmacro ein:mumamo-define-chunk (name)
  143. (let ((funcname (intern (format "ein:mumamo-chunk-%s" name)))
  144. (mode (intern (format "ein:mumamo-%s-mode" name)))
  145. (cell-p (intern (format "ein:%s-p" name))))
  146. `(defun ,funcname (pos max)
  147. (mumamo-possible-chunk-forward
  148. pos max
  149. (lambda (pos max) "CHUNK-START-FUN"
  150. (ein:log 'blather "CHUNK-START-FUN(pos=%s max=%s)" pos max)
  151. (ein:aif (ein:mumamo-find-edge pos max nil #',cell-p)
  152. (list it (if (functionp ,mode)
  153. ,mode
  154. ein:mumamo-fallback-mode)
  155. nil)))
  156. (lambda (pos max) "CHUNK-END-FUN"
  157. (ein:log 'blather "CHUNK-END-FUN(pos=%s max=%s)" pos max)
  158. (ein:mumamo-find-edge pos max t #',cell-p))))))
  159. (ein:mumamo-define-chunk codecell)
  160. (ein:mumamo-define-chunk textcell)
  161. (ein:mumamo-define-chunk htmlcell)
  162. (ein:mumamo-define-chunk markdowncell)
  163. (ein:mumamo-define-chunk rawcell)
  164. (ein:mumamo-define-chunk headingcell)
  165. (defun ein:mumamo-find-edge (pos max end cell-p)
  166. "Helper function for `ein:mumamo-chunk-codecell'.
  167. Return the point of beginning of the input element of cell after
  168. the point POS. Return `nil' if it cannot be found before the point
  169. MAX. If END is non-`nil', end of the input element is returned."
  170. (ein:log 'blather "EIN:MUMAMO-FIND-EDGE(pos=%s max=%s end=%s cell-p=%s)"
  171. pos max end cell-p)
  172. (let* ((ewoc-node
  173. (ein:worksheet-get-nearest-cell-ewoc-node pos max cell-p))
  174. (_ (ein:log 'blather "(null ewoc-node) = %s" (null ewoc-node)))
  175. (cell (ein:aand ewoc-node
  176. (ein:$node-data (ewoc-data it))))
  177. (_ (ein:log 'blather "(null cell) = %s" (null cell)))
  178. (find
  179. (lambda (c)
  180. (ein:aand c
  181. (ein:cell-element-get it (if end :after-input :input))
  182. (progn
  183. (ein:log 'blather "(null it) = %s" (null it))
  184. (ewoc-location it))
  185. (if end it (1+ it)))))
  186. (input-pos (funcall find cell)))
  187. (ein:log 'blather "input-pos (1) = %s" input-pos)
  188. (when (and input-pos (< input-pos pos))
  189. (setq input-pos (ein:aand (ein:cell-next cell)
  190. (when (funcall cell-p it) (funcall find it)))))
  191. (ein:log 'blather "input-pos (2) = %s" input-pos)
  192. (when (and input-pos (> input-pos max))
  193. (setq input-pos nil))
  194. (ein:log 'blather "input-pos (3) = %s" input-pos)
  195. input-pos))
  196. (provide 'ein-mumamo)
  197. ;;; ein-mumamo.el ends here