ein-worksheet.el 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. ;;; ein-worksheet.el --- Worksheet module
  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-worksheet.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-worksheet.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. ;;
  17. ;;; Code:
  18. (eval-when-compile (require 'cl))
  19. (require 'eieio)
  20. (require 'ewoc)
  21. (require 'ein-core)
  22. (require 'ein-cell)
  23. (require 'ein-kill-ring)
  24. ;;; Configuration
  25. (define-obsolete-variable-alias
  26. 'ein:notebook-enable-undo 'ein:worksheet-enable-undo "0.2.0")
  27. (defcustom ein:worksheet-enable-undo 'yes
  28. "Configure undo in notebook buffers.
  29. `no' : symbol
  30. Do not use undo in notebook buffers. It is the safest option.
  31. `yes' : symbol
  32. Enable undo in notebook buffers. You can't undo after
  33. modification of cell (execution, add, remove, etc.). This
  34. is default.
  35. `full' : symbol
  36. Enable full undo in notebook buffers. It is powerful but
  37. sometime (typically after the cell specific commands) undo
  38. mess up notebook buffer. Use it on your own risk. When the
  39. buffer is messed up, you can just redo and continue editing,
  40. or save it once and reopen it if you want to be careful.
  41. You need to reopen the notebook buffer to reflect the change of
  42. this value."
  43. :type '(choice (const :tag "No" no)
  44. (const :tag "Yes" yes)
  45. (const :tag "Full" full))
  46. :group 'ein)
  47. ;;; Configuration getter
  48. (defun ein:worksheet-empty-undo-maybe ()
  49. "Empty `buffer-undo-list' if `ein:worksheet-enable-undo' is `yes'."
  50. (when (eq ein:worksheet-enable-undo 'yes)
  51. (setq buffer-undo-list nil)))
  52. ;;; Class and variable
  53. (defvar ein:worksheet-buffer-name-template "*ein: %s/%s*")
  54. (defclass ein:worksheet ()
  55. ((nbformat :initarg :nbformat :type integer)
  56. (get-notebook-name :initarg :get-notebook-name :type cons)
  57. ;; This slot introduces too much complexity so therefore must be
  58. ;; removed later. This is here only for backward compatible
  59. ;; reason.
  60. (discard-output-p :initarg :discard-output-p)
  61. (saved-cells :initarg :saved-cells :initform nil
  62. :documentation
  63. "Slot to cache cells for worksheet without buffer")
  64. (dont-save-cells :initarg :dont-save-cells :initform nil :type boolean
  65. :documentation "Don't cache cells when this flag is on.")
  66. (ewoc :initarg :ewoc :type ewoc)
  67. (kernel :initarg :kernel :type ein:$kernel)
  68. (dirty :initarg :dirty :type boolean :initform nil)
  69. (metadata :initarg :metadata :initform nil)
  70. (events :initarg :events)))
  71. (ein:deflocal ein:%worksheet% nil
  72. "Buffer local variable to store an instance of `ein:worksheet'.")
  73. ;;; Initialization of object and buffer
  74. (defun ein:worksheet-new (nbformat get-notebook-name discard-output-p
  75. kernel events &rest args)
  76. (apply #'make-instance 'ein:worksheet
  77. :nbformat nbformat :get-notebook-name get-notebook-name
  78. :discard-output-p discard-output-p :kernel kernel :events events
  79. args))
  80. (defmethod ein:worksheet-bind-events ((ws ein:worksheet))
  81. (with-slots (events) ws
  82. ;; Bind events for sub components:
  83. (mapc (lambda (cell) (oset cell :events events))
  84. (ein:worksheet-get-cells ws))))
  85. (defun ein:worksheet-class-bind-events (events)
  86. "Binds event handlers which are not needed to be bound per instance."
  87. (ein:events-on events
  88. 'maybe_reset_undo.Worksheet
  89. (lambda (-ignore- cell)
  90. (ein:with-live-buffer (ein:cell-buffer cell)
  91. (ein:worksheet-empty-undo-maybe))))
  92. (ein:events-on events 'set_next_input.Worksheet
  93. #'ein:worksheet--set-next-input)
  94. (ein:events-on events 'set_dirty.Worksheet #'ein:worksheet--set-dirty))
  95. (defun ein:worksheet--set-next-input (-ignore- data)
  96. (destructuring-bind (&key cell text) data
  97. (ein:with-live-buffer (ein:cell-buffer cell)
  98. (ein:and-let* ((ws ein:%worksheet%)
  99. (new-cell
  100. (ein:worksheet-insert-cell-below ws 'code cell)))
  101. (ein:cell-set-text new-cell text)
  102. (oset ws :dirty t)))))
  103. (defun ein:worksheet--set-dirty (-ignore- data)
  104. "Set dirty flag of worksheet in which CELL in DATA locates."
  105. (destructuring-bind (&key value cell) data
  106. (ein:with-live-buffer (ein:cell-buffer cell)
  107. (ein:worksheet-set-modified-p ein:%worksheet% value))))
  108. (defmethod ein:worksheet-notebook-name ((ws ein:worksheet))
  109. (ein:funcall-packed (oref ws :get-notebook-name)))
  110. (defmethod ein:worksheet-url-or-port ((ws ein:worksheet))
  111. (ein:kernel-url-or-port (oref ws :kernel)))
  112. (defmethod ein:worksheet-name ((ws ein:worksheet))
  113. (plist-get (oref ws :metadata) :name))
  114. (defmethod ein:worksheet-set-name ((ws ein:worksheet) name)
  115. "Set worksheet name.
  116. \(fn ws name)"
  117. (assert (stringp name) nil "NAME must be a string. Got: %S" name)
  118. (oset ws :metadata (plist-put (oref ws :metadata) :name name)))
  119. (defmethod ein:worksheet-full-name ((ws ein:worksheet))
  120. (let ((nb-name (ein:worksheet-notebook-name ws)))
  121. (ein:aif (ein:worksheet-name ws)
  122. (concat nb-name "/" it)
  123. nb-name)))
  124. (defmethod ein:worksheet-buffer ((ws ein:worksheet))
  125. (ein:and-let* (((slot-boundp ws :ewoc))
  126. (ewoc (oref ws :ewoc))
  127. (buffer (ewoc-buffer ewoc))
  128. ((buffer-live-p buffer)))
  129. buffer))
  130. (defmethod ein:worksheet--buffer-name ((ws ein:worksheet))
  131. (format ein:worksheet-buffer-name-template
  132. (ein:worksheet-url-or-port ws)
  133. (ein:worksheet-full-name ws)))
  134. (defmethod ein:worksheet--get-buffer ((ws ein:worksheet))
  135. (or (ein:worksheet-buffer ws)
  136. (generate-new-buffer (ein:worksheet--buffer-name ws))))
  137. (defmethod ein:worksheet-set-buffer-name ((ws ein:worksheet))
  138. (ein:with-live-buffer (ein:worksheet-buffer ws)
  139. (rename-buffer (ein:worksheet--buffer-name ws) t)))
  140. (defmethod ein:worksheet-set-modified-p ((ws ein:worksheet) dirty)
  141. (ein:with-live-buffer (ein:worksheet-buffer ws)
  142. (set-buffer-modified-p dirty))
  143. (oset ws :dirty dirty))
  144. (defmethod ein:worksheet-render ((ws ein:worksheet))
  145. (with-current-buffer (ein:worksheet--get-buffer ws)
  146. (setq ein:%worksheet% ws)
  147. (let ((inhibit-read-only t))
  148. (erase-buffer)
  149. (let ((ewoc (ein:ewoc-create 'ein:worksheet-pp
  150. (ein:propertize-read-only "\n")
  151. nil t))
  152. (cells (oref ws :saved-cells)))
  153. (oset ws :ewoc ewoc)
  154. (if cells
  155. (mapc (lambda (c)
  156. (oset c :ewoc ewoc)
  157. (ein:cell-enter-last c))
  158. cells)
  159. (ein:worksheet-insert-cell-below ws 'code nil t))))
  160. (set-buffer-modified-p nil)
  161. (setq buffer-undo-list nil) ; clear undo history
  162. (when (eq ein:worksheet-enable-undo 'no)
  163. (setq buffer-undo-list t))
  164. (ein:worksheet-bind-events ws)
  165. (ein:worksheet-set-kernel ws)
  166. (ein:log 'info "Worksheet %s is ready" (ein:worksheet-full-name ws))))
  167. (defun ein:worksheet-pp (ewoc-data)
  168. (let ((path (ein:$node-path ewoc-data))
  169. (data (ein:$node-data ewoc-data)))
  170. (case (car path)
  171. (cell (ein:cell-pp (cdr path) data)))))
  172. ;;; Persistance and loading
  173. (defmethod ein:worksheet-from-json ((ws ein:worksheet) data)
  174. (destructuring-bind (&key cells metadata &allow-other-keys) data
  175. (oset ws :metadata metadata)
  176. (oset ws :saved-cells
  177. (mapcar (lambda (data) (ein:cell-from-json data)) cells)))
  178. ws)
  179. (defmethod ein:worksheet-to-json ((ws ein:worksheet))
  180. "Convert worksheet WS into JSON ready alist.
  181. It sets buffer internally so that caller doesn not have to set
  182. current buffer."
  183. (let* ((discard-output-p (oref ws :discard-output-p))
  184. (cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws)
  185. (mapcar (lambda (c)
  186. (ein:cell-to-json
  187. c (ein:funcall-packed discard-output-p c)))
  188. (ein:worksheet-get-cells ws)))))
  189. `((cells . ,(apply #'vector cells))
  190. ,@(ein:aand (oref ws :metadata) `((metadata . ,it))))))
  191. (defmethod ein:worksheet-save-cells ((ws ein:worksheet) &optional deactivate)
  192. "Save cells in worksheet buffer in cache before killing the buffer.
  193. .. warning:: After called with non-nil DEACTIVATE flag is given,
  194. cells in worksheet cannot be used anymore. Use only just
  195. before killing the buffer.
  196. You don't need to set current buffer to call this function.
  197. Do nothing when the worksheet WS has no buffer.
  198. If the `:dont-save-cells' slot is non-nil (meaning that
  199. `ein:worksheet-dont-save-cells' has been called), cells in the
  200. worksheet buffer are not saved. When the DEACTIVATE option is
  201. given, cached cells are deactivated instead of the cells in
  202. buffer. Calling this function unconditionally resets
  203. `:dont-save-cells' flag to nil to make caching work when the
  204. worksheet WS is reopened.
  205. \(fn ws deactivate)"
  206. (when (ein:worksheet-has-buffer-p ws)
  207. (unless (oref ws :dont-save-cells)
  208. (let ((cells (ein:worksheet-get-cells ws)))
  209. (with-current-buffer (ein:worksheet-buffer ws)
  210. (mapc #'ein:cell-save-text cells))
  211. (when deactivate (mapc #'ein:cell-deactivate cells))
  212. (oset ws :saved-cells cells)))
  213. (when deactivate
  214. (mapc #'ein:cell-deactivate (oref ws :saved-cells))))
  215. (oset ws :dont-save-cells nil))
  216. (defmethod ein:worksheet-dont-save-cells ((ws ein:worksheet))
  217. "Turn on `:dont-save-cells' flag so that next call on
  218. `ein:worksheet-save-cells' actually do nothing.
  219. \(fn ws)"
  220. (oset ws :dont-save-cells t))
  221. ;;; Cell indexing, retrieval, etc.
  222. (defmethod ein:worksheet-cell-from-type ((ws ein:worksheet) type &rest args)
  223. "Create a cell of TYPE (symbol or string)."
  224. ;; FIXME: unify type of TYPE to symbol or string.
  225. (apply #'ein:cell-from-type
  226. (format "%s" type)
  227. :ewoc (oref ws :ewoc)
  228. :events (oref ws :events)
  229. args))
  230. (defmethod ein:worksheet-get-cells ((ws ein:worksheet))
  231. (if (ein:worksheet-has-buffer-p ws)
  232. (let* ((ewoc (oref ws :ewoc))
  233. (nodes (ewoc-collect ewoc
  234. (lambda (n) (ein:cell-node-p n 'prompt)))))
  235. (mapcar #'ein:$node-data nodes))
  236. (oref ws :saved-cells)))
  237. (defmethod ein:worksheet-ncells ((ws ein:worksheet))
  238. (length (ein:worksheet-get-cells ws)))
  239. (defun ein:worksheet-get-ewoc (&optional ws)
  240. (ein:aand (or ws ein:%worksheet%) (oref it :ewoc)))
  241. (defun ein:worksheet-get-current-ewoc-node (&optional pos)
  242. (ein:aand (ein:worksheet-get-ewoc) (ewoc-locate it pos)))
  243. (defun ein:worksheet-get-nearest-cell-ewoc-node (&optional pos max cell-p)
  244. (ein:and-let* ((ewoc-node (ein:worksheet-get-current-ewoc-node pos)))
  245. ;; FIXME: can be optimized using the argument `max'
  246. (while (and ewoc-node
  247. (not (and (ein:cell-ewoc-node-p ewoc-node)
  248. (if cell-p
  249. (funcall cell-p
  250. (ein:cell-from-ewoc-node ewoc-node))
  251. t))))
  252. (setq ewoc-node (ewoc-next (oref ein:%worksheet% :ewoc) ewoc-node)))
  253. ewoc-node))
  254. (defun* ein:worksheet-get-current-cell (&key pos noerror
  255. (cell-p #'ein:basecell-child-p))
  256. "Return a cell at POS. If POS is not given, it is assumed be the
  257. current cursor position. When the current buffer is not worksheet
  258. buffer or there is no cell in the current buffer, return `nil'."
  259. (let ((cell (ein:cell-from-ewoc-node
  260. (ein:worksheet-get-current-ewoc-node pos))))
  261. (if (funcall cell-p cell)
  262. cell
  263. (unless noerror
  264. (error "No cell found at pos=%s" pos)))))
  265. (defun ein:worksheet-at-codecell-p ()
  266. (ein:worksheet-get-current-cell :noerror t :cell-p #'ein:codecell-p))
  267. (defun ein:worksheet-get-cells-in-region (beg end)
  268. (ein:clip-list (ein:aand ein:%worksheet% (ein:worksheet-get-cells it))
  269. (ein:worksheet-get-current-cell :pos beg)
  270. (ein:worksheet-get-current-cell :pos end)))
  271. (defun* ein:worksheet-get-cells-in-region-or-at-point
  272. (&key noerror (cell-p #'ein:basecell-child-p))
  273. (or (ein:filter cell-p
  274. (if (region-active-p)
  275. (ein:worksheet-get-cells-in-region (region-beginning)
  276. (region-end))
  277. (list (ein:worksheet-get-current-cell))))
  278. (unless noerror
  279. (error "Cell not found"))))
  280. ;;; Insertion and deletion of cells
  281. (defun ein:worksheet--get-ws-or-error ()
  282. (or ein:%worksheet% (error "Not in worksheet buffer.")))
  283. (defun ein:worksheet-focus-cell ()
  284. (ein:aand (ein:worksheet-get-current-cell :noerror t) (ein:cell-goto it)))
  285. (defun ein:worksheet-delete-cell (ws cell &optional focus)
  286. "Delete a cell. \(WARNING: no undo!)
  287. This command has no key binding because there is no way to undo
  288. deletion. Use kill to play on the safe side.
  289. If you really want use this command, you can do something like this
  290. \(but be careful when using it!)::
  291. \(define-key ein:notebook-mode-map \"\\C-c\\C-d\"
  292. 'ein:worksheet-delete-cell)"
  293. (interactive (list (ein:worksheet--get-ws-or-error)
  294. (ein:worksheet-get-current-cell)
  295. t))
  296. (let ((inhibit-read-only t)
  297. (buffer-undo-list t)) ; disable undo recording
  298. (apply #'ewoc-delete
  299. (oref ws :ewoc)
  300. (ein:cell-all-element cell)))
  301. (oset ws :dirty t)
  302. (ein:worksheet-empty-undo-maybe)
  303. (when focus (ein:worksheet-focus-cell)))
  304. (defun ein:worksheet-kill-cell (ws cells &optional focus)
  305. "Kill (\"cut\") the cell at point or cells in region.
  306. Note that the kill-ring for cells is not shared with the default
  307. kill-ring of Emacs (kill-ring for texts)."
  308. (interactive (list (ein:worksheet--get-ws-or-error)
  309. (ein:worksheet-get-cells-in-region-or-at-point)
  310. t))
  311. (when cells
  312. (mapc (lambda (c)
  313. (ein:cell-save-text c)
  314. (ein:worksheet-delete-cell ws c)
  315. (ein:cell-deactivate c))
  316. cells)
  317. (ein:kill-new cells)
  318. (when focus
  319. (deactivate-mark)
  320. (ein:worksheet-focus-cell))))
  321. (defun ein:worksheet-copy-cell (cells)
  322. "Copy the cell at point. (Put the current cell into the kill-ring.)"
  323. (interactive
  324. (list (when (ein:worksheet--get-ws-or-error)
  325. (prog1 (ein:worksheet-get-cells-in-region-or-at-point)
  326. (deactivate-mark)))))
  327. (let ((cells (mapcar
  328. (lambda (c)
  329. (ein:cell-deactivate (ein:cell-copy c))) cells)))
  330. (ein:log 'info "%s cells are copied." (length cells))
  331. (ein:kill-new cells)))
  332. (defun ein:worksheet-insert-clone-below (ws cell pivot)
  333. (let ((clone (ein:cell-copy cell)))
  334. ;; Cell can be from another buffer, so reset `ewoc'.
  335. (oset clone :ewoc (oref ws :ewoc))
  336. (ein:worksheet-insert-cell-below ws clone pivot)
  337. clone))
  338. (defun ein:worksheet-yank-cell (ws &optional n)
  339. "Insert (\"paste\") the latest killed cell.
  340. Prefixes are act same as the normal `yank' command."
  341. (interactive (list (ein:worksheet--get-ws-or-error)
  342. (let ((arg current-prefix-arg))
  343. (cond ((listp arg) 0)
  344. ((eq arg '-) -2)
  345. (t (1- arg))))))
  346. (let* ((cell (ein:worksheet-get-current-cell :noerror t)) ; can be nil
  347. (killed (ein:current-kill n)))
  348. (loop for c in killed
  349. with last = cell
  350. do (setq last (ein:worksheet-insert-clone-below ws c last))
  351. finally (ein:cell-goto last))))
  352. (defun ein:worksheet-maybe-new-cell (ws type-or-cell)
  353. "Return TYPE-OR-CELL as-is if it is a cell, otherwise return a new cell."
  354. (let ((cell (if (ein:basecell-child-p type-or-cell)
  355. type-or-cell
  356. (ein:worksheet-cell-from-type ws type-or-cell))))
  357. ;; When newly created or copied, kernel is not attached or not the
  358. ;; kernel of this worksheet. So reset it here.
  359. (when (ein:codecell-p cell)
  360. (oset cell :kernel (oref ws :kernel)))
  361. (oset cell :events (oref ws :events))
  362. cell))
  363. (defun ein:worksheet-insert-cell-below (ws type-or-cell pivot &optional focus)
  364. "Insert cell below. Insert markdown cell instead of code cell
  365. when the prefix argument is given.
  366. When used as a lisp function, insert a cell of TYPE-OR-CELL just
  367. after PIVOT and return the new cell."
  368. (interactive (list (ein:worksheet--get-ws-or-error)
  369. (if current-prefix-arg 'markdown 'code)
  370. (ein:worksheet-get-current-cell :noerror t) ; can be nil
  371. t))
  372. (let ((cell (ein:worksheet-maybe-new-cell ws type-or-cell)))
  373. (cond
  374. ((= (ein:worksheet-ncells ws) 0)
  375. (ein:cell-enter-last cell))
  376. (pivot
  377. (ein:cell-insert-below pivot cell))
  378. (t (error
  379. "PIVOT is `nil' but ncells != 0. There is something wrong...")))
  380. (ein:worksheet-empty-undo-maybe)
  381. (oset ws :dirty t)
  382. (when focus (ein:cell-goto cell))
  383. cell))
  384. (defun ein:worksheet-insert-cell-above (ws type-or-cell pivot &optional focus)
  385. "Insert cell above. Insert markdown cell instead of code cell
  386. when the prefix argument is given.
  387. See also: `ein:worksheet-insert-cell-below'."
  388. (interactive (list (ein:worksheet--get-ws-or-error)
  389. (if current-prefix-arg 'markdown 'code)
  390. (ein:worksheet-get-current-cell :noerror t) ; can be nil
  391. t))
  392. (let ((cell (ein:worksheet-maybe-new-cell ws type-or-cell)))
  393. (cond
  394. ((< (ein:worksheet-ncells ws) 2)
  395. (ein:cell-enter-first cell))
  396. (pivot
  397. (let ((prev-cell (ein:cell-prev pivot)))
  398. (if prev-cell
  399. (ein:cell-insert-below prev-cell cell)
  400. (ein:cell-enter-first cell))))
  401. (t (error
  402. "PIVOT is `nil' but ncells > 0. There is something wrong...")))
  403. (ein:worksheet-empty-undo-maybe)
  404. (oset ws :dirty t)
  405. (when focus (ein:cell-goto cell))
  406. cell))
  407. (defun ein:worksheet-toggle-cell-type (ws cell &optional focus)
  408. "Toggle the cell type of the cell at point.
  409. Use `ein:worksheet-change-cell-type' to change the cell type
  410. directly."
  411. (interactive (list (ein:worksheet--get-ws-or-error)
  412. (ein:worksheet-get-current-cell)
  413. t))
  414. (let ((type (case (oref ws :nbformat)
  415. (2 (ein:case-equal (oref cell :cell-type)
  416. (("code") "markdown")
  417. (("markdown") "code")))
  418. (3 (ein:case-equal (oref cell :cell-type)
  419. (("code") "markdown")
  420. (("markdown") "raw")
  421. (("raw") "heading")
  422. (("heading") "code"))))))
  423. (let ((relpos (ein:cell-relative-point cell))
  424. (new (ein:cell-convert-inplace cell type)))
  425. (when (ein:codecell-p new)
  426. (oset new :kernel (oref ws :kernel)))
  427. (ein:worksheet-empty-undo-maybe)
  428. (when focus (ein:cell-goto new relpos)))))
  429. (defun ein:worksheet-change-cell-type (ws cell type &optional level focus)
  430. "Change the cell type of the current cell.
  431. Prompt will appear in the minibuffer.
  432. When used in as a Lisp function, TYPE (string) should be chose
  433. from \"code\", \"markdown\", \"raw\" and \"heading\". LEVEL is
  434. an integer used only when the TYPE is \"heading\"."
  435. (interactive
  436. (let* ((ws (ein:worksheet--get-ws-or-error))
  437. (cell (ein:worksheet-get-current-cell))
  438. (choices (case (oref ws :nbformat)
  439. (2 "cm")
  440. (3 "cmr123456")))
  441. (key (ein:ask-choice-char
  442. (format "Cell type [%s]: " choices) choices))
  443. (type (case key
  444. (?c "code")
  445. (?m "markdown")
  446. (?r "raw")
  447. (t "heading")))
  448. (level (when (equal type "heading")
  449. (string-to-number (char-to-string key)))))
  450. (list ws cell type level t)))
  451. (let ((relpos (ein:cell-relative-point cell))
  452. (new (ein:cell-convert-inplace cell type)))
  453. (when (ein:codecell-p new)
  454. (oset new :kernel (oref ws :kernel)))
  455. (when level
  456. (ein:cell-change-level new level))
  457. (ein:worksheet-empty-undo-maybe)
  458. (when focus (ein:cell-goto new relpos))))
  459. (defun ein:worksheet-split-cell-at-point (ws cell &optional no-trim focus)
  460. "Split cell at current position. Newlines at the splitting
  461. point will be removed. This can be omitted by giving a prefix
  462. argument \(C-u)."
  463. (interactive (list (ein:worksheet--get-ws-or-error)
  464. (ein:worksheet-get-current-cell)
  465. current-prefix-arg
  466. t))
  467. ;; FIXME: should I inhibit undo?
  468. (let* ((beg (set-marker (make-marker) (ein:cell-input-pos-min cell)))
  469. (pos (point-marker))
  470. (head (buffer-substring beg pos))
  471. (new (ein:worksheet-insert-cell-above ws
  472. (oref cell :cell-type)
  473. cell)))
  474. (when (ein:headingcell-p cell)
  475. (ein:cell-change-level new (oref cell :level)))
  476. (delete-region beg pos)
  477. (unless no-trim
  478. (setq head (ein:trim-right head "\n"))
  479. (save-excursion
  480. (goto-char pos)
  481. (let ((end (set-marker (make-marker) (ein:cell-input-pos-max cell))))
  482. (while (and (looking-at-p "\n") (< (point) end))
  483. (delete-char 1)))))
  484. (ein:cell-set-text new head)
  485. (ein:worksheet-empty-undo-maybe)
  486. (when focus (ein:cell-goto cell))))
  487. (defun ein:worksheet-merge-cell (ws cell &optional next focus)
  488. "Merge previous cell into current cell.
  489. If prefix is given, merge current cell into next cell."
  490. (interactive (list (ein:worksheet--get-ws-or-error)
  491. (ein:worksheet-get-current-cell)
  492. current-prefix-arg
  493. t))
  494. (unless next
  495. (setq cell (ein:cell-prev cell))
  496. (unless cell (error "No previous cell"))
  497. (ein:cell-goto cell))
  498. (let* ((next-cell (ein:cell-next cell))
  499. (head (ein:cell-get-text cell)))
  500. (assert next-cell nil "No cell to merge.")
  501. (ein:worksheet-delete-cell ws cell)
  502. (save-excursion
  503. (goto-char (ein:cell-input-pos-min next-cell))
  504. (insert head "\n"))
  505. (ein:worksheet-empty-undo-maybe)
  506. (when focus (ein:cell-goto next-cell))))
  507. ;;; Cell selection.
  508. (defun* ein:worksheet-next-input-cell (ewoc-node &optional up (nth 1))
  509. "Return a cell containing the next input node after EWOC-NODE.
  510. When UP is non-`nil', do the same for the *previous* input node.
  511. When NTH is specified, return NTH cell. Note that this function is
  512. *not* defined for NTH=0; it returns nil."
  513. (unless (= nth 0)
  514. (when (< nth 0)
  515. (setq nth (* nth -1))
  516. (setq up (not up)))
  517. (let ((cell (ein:worksheet-next-input-cell-1 ewoc-node up)))
  518. (loop repeat (1- nth)
  519. with next = (if up #'ein:cell-prev #'ein:cell-next)
  520. if (funcall next cell)
  521. do (setq cell it)
  522. else
  523. return nil)
  524. cell)))
  525. (defun ein:worksheet-next-input-cell-1 (ewoc-node &optional up)
  526. (let* ((ewoc-data (ewoc-data ewoc-node))
  527. (cell (ein:$node-data ewoc-data))
  528. (path (ein:$node-path ewoc-data))
  529. (element (nth 1 path)))
  530. (if (memql element (if up '(output footer) '(prompt)))
  531. cell
  532. (funcall (if up #'ein:cell-prev #'ein:cell-next) cell))))
  533. (defun ein:worksheet-goto-input (ewoc-node up)
  534. (ein:aif (ein:worksheet-next-input-cell ewoc-node up)
  535. (ein:cell-goto it)
  536. (error "No %s input!" (if up "previous" "next"))))
  537. (defun ein:worksheet-goto-next-input (ewoc-node)
  538. (interactive (list (and (ein:worksheet--get-ws-or-error)
  539. (ein:worksheet-get-current-ewoc-node))))
  540. (ein:worksheet-goto-input ewoc-node nil))
  541. (defun ein:worksheet-goto-prev-input (ewoc-node)
  542. (interactive (list (and (ein:worksheet--get-ws-or-error)
  543. (ein:worksheet-get-current-ewoc-node))))
  544. (ein:worksheet-goto-input ewoc-node t))
  545. (defun ein:worksheet-goto-next-cell-element (&optional nth up relpos prop)
  546. "Go to NTH next cell element named PROP and shift cursor by RELPOS.
  547. Go to previous cell if UP is t.
  548. Return t when the movement is succeeded."
  549. (unless prop (setq prop :input))
  550. (ein:and-let* ((current-node (ein:worksheet-get-current-ewoc-node))
  551. (current-cell (ein:cell-from-ewoc-node current-node))
  552. (target-cell
  553. (if (and (= nth 1)
  554. (eq (ein:cell-element-get current-cell :input)
  555. current-node)
  556. (not (and up
  557. (= (1+ (ewoc-location current-node))
  558. (point)))))
  559. current-cell
  560. (ein:worksheet-next-input-cell current-node up nth))))
  561. (ein:cell-goto target-cell relpos prop)
  562. t))
  563. (defun ein:worksheet-beginning-of-cell-input (&optional arg)
  564. "Move backward to the beginning of a cell.
  565. This function is for `beginning-of-defun-function', so behaves
  566. similarly with `beginning-of-defun'.
  567. It is set in `ein:notebook-multilang-mode'."
  568. (ein:worksheet-goto-next-cell-element (or arg 1) t))
  569. (defun ein:worksheet-end-of-cell-input (&optional arg)
  570. "Move forward to the end of a cell.
  571. This function is for `end-of-defun-function', so behaves
  572. similarly with `end-of-defun'.
  573. It is set in `ein:notebook-multilang-mode'."
  574. (ein:worksheet-goto-next-cell-element (or arg 1) nil 0 :after-input))
  575. ;;; Cell movement
  576. (defun ein:worksheet-move-cell (ws cell up)
  577. (ein:aif (if up (ein:cell-prev cell) (ein:cell-next cell))
  578. (let ((inhibit-read-only t)
  579. (pivot-cell it))
  580. (ein:cell-save-text cell)
  581. (ein:worksheet-delete-cell ws cell)
  582. (funcall (if up
  583. #'ein:worksheet-insert-cell-above
  584. #'ein:worksheet-insert-cell-below)
  585. ws cell pivot-cell)
  586. (ein:cell-goto cell)
  587. (oset ws :dirty t))
  588. (error "No %s cell" (if up "previous" "next"))))
  589. (defun ein:worksheet-move-cell-up (ws cell)
  590. (interactive (list (ein:worksheet--get-ws-or-error)
  591. (ein:worksheet-get-current-cell)))
  592. (ein:worksheet-move-cell ws cell t))
  593. (defun ein:worksheet-move-cell-down (ws cell)
  594. (interactive (list (ein:worksheet--get-ws-or-error)
  595. (ein:worksheet-get-current-cell)))
  596. (ein:worksheet-move-cell ws cell nil))
  597. ;;; Cell collapsing and output clearing
  598. (defun ein:worksheet-toggle-output (ws cell)
  599. "Toggle the visibility of the output of the cell at point.
  600. This does not alter the actual data stored in the cell."
  601. (interactive (list (ein:worksheet--get-ws-or-error)
  602. (ein:worksheet-get-current-cell
  603. :cell-p #'ein:codecell-p)))
  604. (ein:cell-toggle-output cell)
  605. (ein:worksheet-empty-undo-maybe)
  606. (oset ws :dirty t))
  607. (defun ein:worksheet-set-output-visibility-all (ws &optional collapsed)
  608. "Show all cell output. When prefix is given, hide all cell output."
  609. (interactive (list (ein:worksheet--get-ws-or-error) current-prefix-arg))
  610. (when collapsed (setq collapsed t)) ; force it to be a boolean
  611. (mapc (lambda (c)
  612. (when (ein:codecell-p c) (ein:cell-set-collapsed c collapsed)))
  613. (ein:worksheet-get-cells ws))
  614. (ein:worksheet-empty-undo-maybe)
  615. (oset ws :dirty t))
  616. (defun ein:worksheet-clear-output (cell &optional preserve-input-prompt)
  617. "Clear output from the current cell at point.
  618. Do not clear input prompt when the prefix argument is given."
  619. (interactive (list (ein:worksheet-get-current-cell
  620. :cell-p #'ein:codecell-p)
  621. current-prefix-arg))
  622. (ein:cell-clear-output cell t t t)
  623. (unless preserve-input-prompt
  624. (ein:cell-set-input-prompt cell))
  625. (ein:worksheet-empty-undo-maybe))
  626. (defun ein:worksheet-clear-all-output (ws &optional preserve-input-prompt)
  627. "Clear output from all cells.
  628. Do not clear input prompts when the prefix argument is given."
  629. (interactive (list (ein:worksheet--get-ws-or-error) current-prefix-arg))
  630. (mapc (lambda (c) (ein:worksheet-clear-output c preserve-input-prompt))
  631. (ein:filter #'ein:codecell-p (ein:worksheet-get-cells ws))))
  632. ;;; Kernel related things
  633. (defmethod ein:worksheet-set-kernel ((ws ein:worksheet))
  634. (mapc (lambda (cell) (oset cell :kernel (oref ws :kernel)))
  635. (ein:filter #'ein:codecell-p (ein:worksheet-get-cells ws))))
  636. (defun ein:worksheet-execute-cell (ws cell)
  637. "Execute code type CELL."
  638. (interactive (list (ein:worksheet--get-ws-or-error)
  639. (ein:worksheet-get-current-cell
  640. :cell-p #'ein:codecell-p)))
  641. (ein:kernel-if-ready (oref ws :kernel)
  642. (ein:cell-execute cell)
  643. (oset ws :dirty t)
  644. cell))
  645. (defun ein:worksheet-execute-cell-and-goto-next (ws cell &optional insert)
  646. "Execute cell at point if it is a code cell and move to the
  647. next cell, or insert if none."
  648. (interactive (list (ein:worksheet--get-ws-or-error)
  649. (ein:worksheet-get-current-cell)))
  650. (when (ein:codecell-p cell)
  651. (ein:worksheet-execute-cell ws cell))
  652. (ein:aif (and (not insert) (ein:cell-next cell))
  653. (ein:cell-goto it)
  654. (ein:worksheet-insert-cell-below ws 'code cell t)))
  655. (defun ein:worksheet-execute-cell-and-insert-below (ws cell)
  656. "Execute cell at point if it is a code cell and insert a
  657. cell bellow."
  658. (interactive (list (ein:worksheet--get-ws-or-error)
  659. (ein:worksheet-get-current-cell)))
  660. (ein:worksheet-execute-cell-and-goto-next ws cell t))
  661. (defun ein:worksheet-execute-all-cell (ws)
  662. "Execute all cells in the current worksheet buffer."
  663. (interactive (list (ein:worksheet--get-ws-or-error)))
  664. (mapc #'ein:cell-execute
  665. (ein:filter #'ein:codecell-p (ein:worksheet-get-cells ws))))
  666. (defun ein:worksheet-insert-last-input-history (ws cell index)
  667. "Insert INDEX-th previous history into CELL in worksheet WS."
  668. (ein:kernel-history-request
  669. (oref ws :kernel)
  670. (list
  671. :history_reply
  672. (cons
  673. (lambda (cell content -metadata-not-used-)
  674. (destructuring-bind (session line-number input)
  675. (car (plist-get content :history))
  676. (if (eq (ein:worksheet-get-current-cell) cell)
  677. (ein:cell-set-text cell input)
  678. (ein:log 'warning
  679. "Cursor moved from the cell after history request."))
  680. (ein:log 'info "Input history inserted: session:%d line:%d"
  681. session line-number)))
  682. cell))
  683. :hist-access-type "range"
  684. :session 0
  685. :start (- index)
  686. :stop (- 1 index)))
  687. (defvar ein:worksheet--history-index 1)
  688. (defun ein:worksheet--get-history-index (inc)
  689. "Increment history index by (possibly negative) INC.
  690. Get history index for `ein:worksheet-previous-input-history' and
  691. `ein:worksheet-next-input-history'. Raise error if caller tries
  692. to decrement index to less than or equal to 1."
  693. (if (or (eq last-command 'ein:worksheet-previous-input-history)
  694. (eq last-command 'ein:worksheet-next-input-history))
  695. (progn
  696. (setq ein:worksheet--history-index
  697. (+ ein:worksheet--history-index inc))
  698. (when (< ein:worksheet--history-index 1)
  699. (setq ein:worksheet--history-index 1)
  700. (error "This is the latest input"))
  701. ein:worksheet--history-index)
  702. (setq ein:worksheet--history-index 1)))
  703. (defun ein:worksheet-previous-input-history (ws cell index)
  704. "Insert the previous input in the execution history.
  705. You can go back further in the history by repeating this command.
  706. Use `ein:worksheet-next-input-history' to go forward in the
  707. history."
  708. (interactive (list (ein:worksheet--get-ws-or-error)
  709. (ein:worksheet-get-current-cell)
  710. (ein:worksheet--get-history-index +1)))
  711. (ein:worksheet-insert-last-input-history ws cell index))
  712. (defun ein:worksheet-next-input-history (ws cell index)
  713. "Insert next input in the execution history.
  714. You can go forward further in the history by repeating this
  715. command. Use `ein:worksheet-previous-input-history' to go back
  716. in the history."
  717. (interactive (list (ein:worksheet--get-ws-or-error)
  718. (ein:worksheet-get-current-cell)
  719. (ein:worksheet--get-history-index -1)))
  720. (ein:worksheet-insert-last-input-history ws cell index))
  721. ;;; Metadata
  722. (defun ein:worksheet-rename-sheet (ws name)
  723. "Change worksheet name (*not* notebook name)."
  724. (interactive (let ((ws (ein:worksheet--get-ws-or-error)))
  725. (list ws
  726. (read-from-minibuffer
  727. "New worksheet name: " (ein:worksheet-name ws)))))
  728. (unless (equal name (or (ein:worksheet-name ws) ""))
  729. (ein:worksheet-set-name ws name)
  730. (ein:worksheet-set-modified-p ws t)
  731. (ein:worksheet-set-buffer-name ws)))
  732. ;;; Generic getter
  733. (defun ein:get-url-or-port--worksheet ()
  734. (when (ein:worksheet-p ein:%worksheet%)
  735. (ein:worksheet-url-or-port ein:%worksheet%)))
  736. (defun ein:get-kernel--worksheet ()
  737. (when (ein:worksheet-p ein:%worksheet%) (oref ein:%worksheet% :kernel)))
  738. (defun ein:get-cell-at-point--worksheet ()
  739. (ein:worksheet-get-current-cell :noerror t))
  740. (defun ein:get-traceback-data--worksheet ()
  741. (ein:aand (ein:get-cell-at-point--worksheet) (ein:cell-get-tb-data it)))
  742. ;;; Predicate
  743. (defun ein:worksheet-buffer-p ()
  744. "Return non-`nil' if the current buffer is a worksheet buffer."
  745. ein:%worksheet%)
  746. (defmethod ein:worksheet-has-buffer-p ((ws ein:worksheet))
  747. (ein:aand (ein:worksheet-buffer ws) (buffer-live-p it)))
  748. (defmethod ein:worksheet-modified-p ((ws ein:worksheet))
  749. (let ((buffer (ein:worksheet-buffer ws)))
  750. (and (buffer-live-p buffer)
  751. (or (oref ws :dirty)
  752. (buffer-modified-p buffer)))))
  753. ;;; Utility commands
  754. (defun ein:worksheet-dedent-cell-text (cell)
  755. "Dedent text in CELL."
  756. (interactive (list (ein:worksheet-get-current-cell)))
  757. (let* ((beg (ein:cell-input-pos-min cell))
  758. (end (ein:cell-input-pos-max cell)))
  759. (indent-rigidly
  760. beg end (- (ein:find-leftmot-column beg end)))))
  761. ;;; Auto-execution
  762. (defun ein:worksheet-toggle-autoexec (cell)
  763. "Toggle auto-execution flag of the cell at point."
  764. (interactive (list (ein:worksheet-get-current-cell #'ein:codecell-p)))
  765. (ein:cell-toggle-autoexec cell))
  766. (defun ein:worksheet-turn-on-autoexec (cells &optional off)
  767. "Turn on auto-execution flag of the cells in region or cell at point.
  768. When the prefix argument is given, turn off the flag instead.
  769. To use autoexec feature, you need to turn on auto-execution mode
  770. in connected buffers, using the `ein:connect-toggle-autoexec'
  771. command."
  772. (interactive
  773. (list (ein:worksheet-get-cells-in-region-or-at-point
  774. :cell-p #'ein:codecell-p)
  775. current-prefix-arg))
  776. (mapc (lambda (c) (ein:cell-set-autoexec c (not off))) cells)
  777. (ein:log 'info "Turn %s auto-execution flag of %s cells."
  778. (if off "off" "on")
  779. (length cells)))
  780. (defun ein:worksheet-execute-autoexec-cells (ws)
  781. "Execute cells of which auto-execution flag is on.
  782. This function internally sets current buffer to the worksheet
  783. buffer, so you don't need to set current buffer to call this
  784. function."
  785. (interactive (list (ein:worksheet--get-ws-or-error)))
  786. (ein:with-live-buffer (ein:worksheet-buffer ws)
  787. (ein:kernel-if-ready (oref ws :kernel)
  788. (mapc #'ein:cell-execute
  789. (ein:filter #'ein:cell-autoexec-p
  790. (ein:worksheet-get-cells ws))))))
  791. ;;; Imenu
  792. (defun ein:worksheet-imenu-create-index ()
  793. "`imenu-create-index-function' for notebook buffer."
  794. ;; As Imenu does not provide the way to represent level *and*
  795. ;; position, use #'s to do that.
  796. (loop for cell in (when (ein:worksheet-p ein:%worksheet%)
  797. (ein:filter #'ein:headingcell-p
  798. (ein:worksheet-get-cells ein:%worksheet%)))
  799. for sharps = (loop repeat (oref cell :level) collect "#")
  800. for text = (ein:cell-get-text cell)
  801. for name = (ein:join-str "" (append sharps (list " " text)))
  802. collect (cons name (ein:cell-input-pos-min cell))))
  803. (defun ein:worksheet-imenu-setup ()
  804. "Called via notebook mode hooks."
  805. (setq imenu-create-index-function #'ein:worksheet-imenu-create-index))
  806. ;;; Workarounds
  807. (defadvice fill-paragraph (around ein:worksheet-fill-paragraph activate)
  808. "Prevent \"Text is read-only\" error when filling paragraph in
  809. EIN worksheet."
  810. (if ein:%worksheet%
  811. (let* ((cell (ein:worksheet-get-current-cell))
  812. (beg (copy-marker (ein:cell-input-pos-min cell))))
  813. (save-excursion
  814. (goto-char beg)
  815. (insert "\n"))
  816. (unwind-protect
  817. ad-do-it
  818. (save-excursion
  819. (goto-char beg)
  820. (delete-char 1))))
  821. ad-do-it))
  822. (provide 'ein-worksheet)
  823. ;;; ein-worksheet.el ends here