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.

276 lines
11 KiB

  1. ;;; ein-cell-edit.el --- Notebook cell editing
  2. ;; Copyright (C) 2016 John M. Miller
  3. ;; Author: John Miller <millejoh at mac.com>
  4. ;; This file is NOT part of GNU Emacs.
  5. ;; ein-cell-edit.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-cell-edit.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-worksheet.el. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; This code inspired by borrowing from org-src.el.
  17. ;;; Code:
  18. (require 'ein-cell)
  19. (autoload 'julia-mode "julia-mode")
  20. (autoload 'markdown-mode "markdown-mode")
  21. (autoload 'R-mode "ess-r-mode")
  22. (autoload 'org-src--remove-overlay "org-src")
  23. (autoload 'org-src-switch-to-buffer "org-src")
  24. (defvar ein:src--cell nil)
  25. (defvar ein:src--ws nil)
  26. (defvar ein:src--allow-write-back t)
  27. (defvar ein:src--overlay nil)
  28. (defvar ein:src--saved-window-config nil)
  29. (declare-function ein:notebook--get-nb-or-error "ein-notebook" ())
  30. (defvar ein:edit-cell-mode-map
  31. (let ((map (make-sparse-keymap)))
  32. (define-key map "\C-c'" 'ein:edit-cell-exit)
  33. (define-key map "\C-c\C-k" 'ein:edit-cell-abort)
  34. (define-key map "\C-c\C-c" 'ein:edit-cell-save-and-execute)
  35. (define-key map "\C-x\C-s" 'ein:edit-cell-save)
  36. (define-key map "\C-c\C-x" 'ein:edit-cell-view-traceback)
  37. map))
  38. (define-minor-mode ein:edit-cell-mode
  39. "Minor mode for language major mode buffers generated by EIN.
  40. This minor mode is turned on when editing a source code snippet with \\[ein:edit-cell-contents]
  41. \\{ein:edit-cell-mode-map}
  42. ."
  43. nil " EinCell" nil
  44. (set (make-local-variable 'header-line-format)
  45. (substitute-command-keys
  46. "Edit, execute with \\[ein:edit-cell-execute] then exit with \\[ein:edit-cell-exit] \
  47. or abort with \\[ein:edit-cell-abort]"))
  48. ;; Possibly activate various auto-save features (for the edit buffer
  49. ;; or the source buffer).
  50. ;; (when org-edit-src-turn-on-auto-save
  51. ;; (setq buffer-auto-save-file-name
  52. ;; (concat (make-temp-name "org-src-")
  53. ;; (format-time-string "-%Y-%d-%m")
  54. ;; ".txt")))
  55. ;; (unless (or org-src--auto-save-timer (zerop org-edit-src-auto-save-idle-delay))
  56. ;; (setq org-src--auto-save-timer
  57. ;; (run-with-idle-timer
  58. ;; org-edit-src-auto-save-idle-delay t
  59. ;; (lambda ()
  60. ;; (save-excursion
  61. ;; (let (edit-flag)
  62. ;; (dolist (b (buffer-list))
  63. ;; (with-current-buffer b
  64. ;; (when (org-src-edit-buffer-p)
  65. ;; (unless edit-flag (setq edit-flag t))
  66. ;; (when (buffer-modified-p) (org-edit-src-save)))))
  67. ;; (unless edit-flag
  68. ;; (cancel-timer org-src--auto-save-timer)
  69. ;; (setq org-src--auto-save-timer nil))))))))
  70. )
  71. (defun ein:cell-configure-edit-buffer ()
  72. (when (and (bound-and-true-p org-src--from-org-mode) (boundp 'org-src--beg-marker))
  73. (add-hook 'kill-buffer-hook #'org-src--remove-overlay nil 'local)
  74. (if (bound-and-true-p org-src--allow-write-back)
  75. (progn
  76. (setq buffer-offer-save t)
  77. (setq buffer-file-name
  78. (concat (buffer-file-name (marker-buffer org-src--beg-marker))
  79. "[" (buffer-name) "]"))
  80. (setq write-contents-functions '(ein:edit-cell-save)))
  81. (setq buffer-read-only t))))
  82. (defun ein:edit-cell-view-traceback ()
  83. "Jump to traceback, if there is one, for current edit."
  84. (interactive)
  85. (let ((buf (current-buffer))
  86. (cell ein:src--cell))
  87. (with-current-buffer (ein:worksheet--get-buffer ein:src--ws)
  88. (ein:cell-goto cell)
  89. (ein:tb-show))))
  90. (defun ein:edit-cell-save-and-execute ()
  91. "Save, then execute the countents of the EIN source edit buffer
  92. and place results (if any) in output of original notebook cell."
  93. (interactive)
  94. (ein:edit-cell-save)
  95. (when (and (slot-exists-p ein:src--cell 'kernel)
  96. (slot-boundp ein:src--cell 'kernel))
  97. (ein:cell-execute-internal ein:src--cell
  98. (slot-value ein:src--cell 'kernel)
  99. (buffer-string)
  100. :silent nil)))
  101. (defun ein:edit-cell-save ()
  102. "Save contents of EIN source edit buffer back to original notebook
  103. cell."
  104. (interactive)
  105. (set-buffer-modified-p nil)
  106. (let* ((edited-code (buffer-string))
  107. (cell ein:src--cell)
  108. (overlay ein:src--overlay)
  109. (read-only (overlay-get overlay 'modification-hooks)))
  110. (overlay-put overlay 'modification-hooks nil)
  111. (overlay-put overlay 'insert-in-front-hooks nil)
  112. (overlay-put overlay 'insert-behind-hooks nil)
  113. (with-current-buffer (ein:worksheet--get-buffer ein:src--ws)
  114. (ein:cell-set-text cell edited-code))
  115. ;;(setf (slot-value ein:src--cell 'input) edited-code)
  116. (overlay-put overlay 'modification-hooks read-only)
  117. (overlay-put overlay 'insert-in-front-hooks read-only)
  118. (overlay-put overlay 'insert-behind-hooks read-only)))
  119. (defun ein:edit-cell-exit ()
  120. "Close the EIN source edit buffer, saving contents back to the
  121. original notebook cell, unless being called via
  122. `ein:edit-cell-abort'."
  123. (interactive)
  124. (let ((edit-buffer (current-buffer))
  125. (ws ein:src--ws)
  126. (cell ein:src--cell))
  127. (ein:remove-overlay)
  128. (when ein:src--allow-write-back
  129. (ein:edit-cell-save))
  130. (kill-buffer edit-buffer)
  131. (switch-to-buffer-other-window (ein:worksheet--get-buffer ws))
  132. (ein:cell-goto cell)
  133. (when ein:src--saved-window-config
  134. (set-window-configuration ein:src--saved-window-config)
  135. (setq ein:src--saved-window-config nil))))
  136. (defun ein:edit-cell-abort ()
  137. "Abort editing the current cell, contents will revert to
  138. previous value."
  139. (interactive)
  140. (let (ein:src--allow-write-back) (ein:edit-cell-exit)))
  141. (defun ein:construct-cell-edit-buffer-name (bufname cid cell-type)
  142. (concat "*EIN Src " bufname "[ " cid "/" cell-type " ]*" ))
  143. (defun ein:get-mode-for-kernel (kernelspec)
  144. (if (null kernelspec)
  145. 'python ;; FIXME
  146. (intern (ein:$kernelspec-language kernelspec))))
  147. ;; (ein:case-equal (ein:$kernelspec-language kernelspec)
  148. ;; (("julia" "python" "R") )
  149. ;; (t 'python))))
  150. (defun ein:edit-src-continue (e)
  151. (interactive "e")
  152. (mouse-set-point e)
  153. (let ((buf (get-char-property (point) 'edit-buffer)))
  154. (if buf (org-src-switch-to-buffer buf 'continue)
  155. (user-error "No sub-editing buffer for area at point"))))
  156. (defun ein:make-source-overlay (beg end edit-buffer)
  157. "Create overlay between BEG and END positions and return it.
  158. EDIT-BUFFER is the buffer currently editing area between BEG and
  159. END."
  160. (let ((overlay (make-overlay beg end)))
  161. (overlay-put overlay 'face 'secondary-selection)
  162. (overlay-put overlay 'edit-buffer edit-buffer)
  163. (overlay-put overlay 'help-echo
  164. "Click with mouse-1 to switch to buffer editing this segment")
  165. (overlay-put overlay 'face 'secondary-selection)
  166. (overlay-put overlay 'keymap
  167. (let ((map (make-sparse-keymap)))
  168. (define-key map [mouse-1] 'ein:edit-src-continue)
  169. map))
  170. (let ((read-only
  171. (list
  172. (lambda (&rest _)
  173. (user-error
  174. "Cannot modify an area being edited in a dedicated buffer")))))
  175. (overlay-put overlay 'modification-hooks read-only)
  176. (overlay-put overlay 'insert-in-front-hooks read-only)
  177. (overlay-put overlay 'insert-behind-hooks read-only))
  178. overlay))
  179. (defun ein:remove-overlay ()
  180. "Remove overlay from current source buffer."
  181. (when (overlayp ein:src--overlay) (delete-overlay ein:src--overlay)))
  182. (defcustom ein:raw-cell-default-edit-mode 'LaTeX-mode
  183. "The major mode to use when editing a cell of type 'Raw' in the
  184. dedicated edit buffer. By default we use LaTeX-mode."
  185. :type 'symbol
  186. :group 'ein)
  187. (defun ein:edit-cell-contents ()
  188. "Edit the contents of the current cell in a buffer using an
  189. appropriate language major mode. Functionality is very similar to
  190. `org-edit-special'."
  191. (interactive)
  192. (setq ein:src--saved-window-config (current-window-configuration))
  193. (let* ((cell (or (ein:worksheet-get-current-cell)
  194. (error "Must be called from inside an EIN worksheet cell.")))
  195. (nb (ein:notebook--get-nb-or-error))
  196. (ws (ein:worksheet--get-ws-or-error))
  197. (type (slot-value cell 'cell-type))
  198. (name (ein:construct-cell-edit-buffer-name (buffer-name) (ein:cell-id cell) type)))
  199. (ein:aif (get-buffer name)
  200. (switch-to-buffer-other-window it)
  201. (ein:create-edit-cell-buffer name cell nb ws))))
  202. (defun ein:edit-cell-detect-type (contents notebook &optional raw-cell-p)
  203. (if (string-match "^%%\\(.*\\)" contents)
  204. (ein:case-equal (match-string 1 contents)
  205. (("html" "HTML") (html-mode))
  206. (("latex" "LATEX") (LaTeX-mode))
  207. (("ruby") (ruby-mode))
  208. (("sh" "bash") (sh-mode))
  209. (("javascript" "js") (javascript-mode))
  210. (t (funcall ein:raw-cell-default-edit-mode)))
  211. (if raw-cell-p
  212. (funcall ein:raw-cell-default-edit-mode)
  213. (cl-case (ein:get-mode-for-kernel (ein:$notebook-kernelspec notebook))
  214. (julia (julia-mode))
  215. (python (python-mode))
  216. (R (R-mode))))))
  217. (defun ein:create-edit-cell-buffer (name cell notebook worksheet)
  218. (let* ((contents (ein:cell-get-text cell))
  219. (type (slot-value cell 'cell-type))
  220. (buffer (generate-new-buffer-name name))
  221. (overlay (ein:make-source-overlay (ein:cell-input-pos-min cell)
  222. (ein:cell-input-pos-max cell)
  223. buffer)))
  224. (switch-to-buffer-other-window buffer)
  225. (insert contents)
  226. (remove-text-properties (point-min) (point-max)
  227. '(display nil invisible nil intangible nil))
  228. (set-buffer-modified-p nil)
  229. (setq buffer-file-name buffer) ;; Breaks anaconda-mode without this special fix.
  230. (condition-case e
  231. (ein:case-equal type
  232. (("markdown") (markdown-mode))
  233. (("raw") (ein:edit-cell-detect-type contents notebook t))
  234. (("code") (ein:edit-cell-detect-type contents notebook)))
  235. (error (message "Language mode `%s' fails with: %S"
  236. type (nth 1 e))))
  237. (set (make-local-variable 'ein:src--overlay) overlay)
  238. (set (make-local-variable 'ein:src--cell) cell)
  239. (set (make-local-variable 'ein:src--ws) worksheet)
  240. (set (make-local-variable 'ein:src--allow-write-back) t)
  241. (ein:edit-cell-mode)))
  242. (provide 'ein-cell-edit)