ein-notebook.el 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421
  1. ;;; ein-notebook.el --- Notebook 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-notebook.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-notebook.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-notebook.el. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; * Coding rule about current buffer.
  17. ;; A lot of notebook and cell functions touches to current buffer and
  18. ;; it is not ideal to wrap all these functions by `with-current-buffer'.
  19. ;; Therefore, when the function takes `notebook' to the first argument
  20. ;; ("method" function), it is always assumed that the current buffer
  21. ;; is the notebook buffer. **However**, functions called as callback
  22. ;; (via `url-retrieve', for example) must protect themselves by
  23. ;; calling from unknown buffer.
  24. ;;; Code:
  25. (eval-when-compile (require 'cl))
  26. (require 'ewoc)
  27. (eval-when-compile (require 'auto-complete nil t))
  28. (require 'ein-core)
  29. (require 'ein-log)
  30. (require 'ein-node)
  31. (require 'ein-kernel)
  32. (require 'ein-kernelinfo)
  33. (require 'ein-cell)
  34. (require 'ein-worksheet)
  35. (require 'ein-scratchsheet)
  36. (require 'ein-notification)
  37. (require 'ein-completer)
  38. (require 'ein-pager)
  39. (require 'ein-events)
  40. (require 'ein-notification)
  41. (require 'ein-kill-ring)
  42. (require 'ein-query)
  43. (require 'ein-pytools)
  44. ;;; Configuration
  45. (make-obsolete-variable 'ein:notebook-discard-output-on-save nil "0.2.0")
  46. (defcustom ein:notebook-discard-output-on-save 'no
  47. "Configure if the output part of the cell should be saved or not.
  48. .. warning:: This configuration is obsolete now.
  49. Use nbconvert (https://github.com/ipython/nbconvert) to
  50. strip output.
  51. `no' : symbol
  52. Save output. This is the default.
  53. `yes' : symbol
  54. Always discard output.
  55. a function
  56. This function takes two arguments, notebook and cell. Return
  57. `t' to discard output and return `nil' to save. For example,
  58. if you don't want to save image output but other kind of
  59. output, use `ein:notebook-cell-has-image-output-p'.
  60. "
  61. :type '(choice (const :tag "No" 'no)
  62. (const :tag "Yes" 'yes)
  63. )
  64. :group 'ein)
  65. (defun ein:notebook-cell-has-image-output-p (-ignore- cell)
  66. (ein:cell-has-image-ouput-p cell))
  67. (defun ein:notebook-discard-output-p (notebook cell)
  68. "Return non-`nil' if the output must be discarded, otherwise save."
  69. (case ein:notebook-discard-output-on-save
  70. (no nil)
  71. (yes t)
  72. (t (funcall ein:notebook-discard-output-on-save notebook cell))))
  73. ;; As opening/saving notebook treats possibly huge data, define these
  74. ;; timeouts separately:
  75. (defcustom ein:notebook-querty-timeout-open (* 60 1000) ; 1 min
  76. "Query timeout for opening notebook.
  77. If you cannot open large notebook because of timeout error, try
  78. to increase this value. Setting this value to `nil' means to use
  79. global setting. For global setting and more information, see
  80. `ein:query-timeout'."
  81. :type '(choice (integer :tag "Timeout [ms]" 5000)
  82. (const :tag "Use global setting" nil))
  83. :group 'ein)
  84. (defcustom ein:notebook-querty-timeout-save (* 60 1000) ; 1 min
  85. "Query timeout for saving notebook.
  86. Similar to `ein:notebook-querty-timeout-open', but for saving
  87. notebook. For global setting and more information, see
  88. `ein:query-timeout'."
  89. :type '(choice (integer :tag "Timeout [ms]" 5000)
  90. (const :tag "Use global setting" nil))
  91. :group 'ein)
  92. (defcustom ein:helm-kernel-history-search-key nil
  93. "Bind `helm-ein-kernel-history' to this key in notebook mode.
  94. Example::
  95. (setq ein:helm-kernel-history-search-key \"\\M-r\")
  96. This key will be installed in the `ein:notebook-mode-map'."
  97. :type 'boolean
  98. :group 'ein)
  99. (defcustom ein:anything-kernel-history-search-key nil
  100. "Bind `anything-ein-kernel-history' to this key in notebook mode.
  101. Example::
  102. (setq ein:anything-kernel-history-search-key \"\\M-r\")
  103. This key will be installed in the `ein:notebook-mode-map'."
  104. :type 'boolean
  105. :group 'ein)
  106. (defcustom ein:notebook-set-buffer-file-name nil
  107. "[EXPERIMENTAL] Set `buffer-file-name' of notebook buffer."
  108. :type 'boolean
  109. :group 'ein)
  110. (defvar ein:notebook-after-rename-hook nil
  111. "Hooks to run after notebook is renamed successfully.
  112. Current buffer for these functions is set to the notebook buffer.")
  113. ;;; Class and variable
  114. (defvar ein:base-kernel-url "/")
  115. ;; Currently there is no way to know this setting. Maybe I should ask
  116. ;; IPython developers for an API to get this from notebook server.
  117. (defvar ein:notebook-pager-buffer-name-template "*ein:pager %s/%s*")
  118. (defvar ein:notebook-buffer-name-template "*ein: %s/%s*")
  119. (defvar ein:notebook-save-retry-max 1
  120. "Maximum retries for notebook saving.")
  121. (defstruct ein:$notebook
  122. "Hold notebook variables.
  123. `ein:$notebook-url-or-port'
  124. URL or port of IPython server.
  125. `ein:$notebook-notebook-id' : string
  126. uuid string
  127. `ein:$notebook-kernel' : `ein:$kernel'
  128. `ein:$kernel' instance.
  129. `ein:$notebook-kernelinfo' : `ein:kernelinfo'
  130. `ein:kernelinfo' instance.
  131. `ein:$notebook-pager'
  132. Variable for `ein:pager-*' functions. See ein-pager.el.
  133. `ein:$notebook-dirty' : boolean
  134. Set to `t' if notebook has unsaved changes. Otherwise `nil'.
  135. `ein:$notebook-metadata' : plist
  136. Notebook meta data (e.g., notebook name).
  137. `ein:$notebook-name' : string
  138. Notebook name.
  139. `ein:$notebook-nbformat' : integer
  140. Notebook file format version.
  141. `ein:$notebook-nbformat-minor' : integer
  142. Notebook file format version.
  143. `ein:$notebook-events' : `ein:$events'
  144. Event handler instance.
  145. `ein:$notebook-worksheets' : list of `ein:worksheet'
  146. List of worksheets.
  147. `ein:$notebook-scratchsheets' : list of `ein:worksheet'
  148. List of scratch worksheets.
  149. "
  150. url-or-port
  151. notebook-id
  152. kernel
  153. kernelinfo
  154. pager
  155. dirty
  156. metadata
  157. notebook-name
  158. nbformat
  159. nbformat-minor
  160. events
  161. worksheets
  162. scratchsheets
  163. )
  164. (ein:deflocal ein:%notebook% nil
  165. "Buffer local variable to store an instance of `ein:$notebook'.")
  166. (define-obsolete-variable-alias 'ein:notebook 'ein:%notebook% "0.1.2")
  167. ;;; Constructor
  168. (defun ein:notebook-new (url-or-port notebook-id &rest args)
  169. (let ((notebook (apply #'make-ein:$notebook
  170. :url-or-port url-or-port
  171. :notebook-id notebook-id
  172. args)))
  173. notebook))
  174. ;;; Destructor
  175. (defun ein:notebook-del (notebook)
  176. "Destructor for `ein:$notebook'."
  177. (ein:log-ignore-errors
  178. (ein:kernel-del (ein:$notebook-kernel notebook))))
  179. (defun ein:notebook-close-worksheet (notebook ws)
  180. "Close worksheet WS in NOTEBOOK. If WS is the last worksheet,
  181. call notebook destructor `ein:notebook-del'."
  182. (symbol-macrolet ((worksheets (ein:$notebook-worksheets notebook))
  183. (scratchsheets (ein:$notebook-scratchsheets notebook)))
  184. (cond
  185. ((ein:worksheet-p ws) (ein:worksheet-save-cells ws t))
  186. (t (setq scratchsheets (delq ws scratchsheets))))
  187. (unless (or (ein:filter (lambda (x)
  188. (and (not (eq x ws))
  189. (ein:worksheet-has-buffer-p x)))
  190. worksheets)
  191. scratchsheets)
  192. (ein:notebook-del notebook))))
  193. ;;; Notebook utility functions
  194. (defun ein:notebook-buffer (notebook)
  195. "Return the buffer that is associated with NOTEBOOK."
  196. ;; FIXME: Find a better way to define notebook buffer!
  197. ;; For example, the last accessed buffer.
  198. (let ((first-buffer
  199. (lambda (ws-list)
  200. (loop for ws in ws-list if (ein:worksheet-buffer ws) return it))))
  201. (or (funcall first-buffer (ein:$notebook-worksheets notebook))
  202. (funcall first-buffer (ein:$notebook-scratchsheets notebook)))))
  203. (defun ein:notebook-buffer-list (notebook)
  204. "Return the buffers associated with NOTEBOOK's kernel.
  205. The buffer local variable `default-directory' of these buffers
  206. will be updated with kernel's cwd."
  207. (ein:filter #'identity
  208. (mapcar #'ein:worksheet-buffer
  209. (append (ein:$notebook-worksheets notebook)
  210. (ein:$notebook-scratchsheets notebook)))))
  211. (defun ein:notebook--get-nb-or-error ()
  212. (or ein:%notebook% (error "Not in notebook buffer.")))
  213. ;;;###autoload
  214. (defalias 'ein:notebook-name 'ein:$notebook-notebook-name)
  215. (defun ein:notebook-name-getter (notebook)
  216. (cons #'ein:notebook-name notebook))
  217. ;;; Open notebook
  218. (defun ein:notebook-url (notebook)
  219. (ein:notebook-url-from-url-and-id (ein:$notebook-url-or-port notebook)
  220. (ein:$notebook-notebook-id notebook)))
  221. (defun ein:notebook-url-from-url-and-id (url-or-port notebook-id)
  222. (ein:url url-or-port "notebooks" notebook-id))
  223. (defun ein:notebook-pop-to-current-buffer (&rest -ignore-)
  224. "Default callback for `ein:notebook-open'."
  225. (pop-to-buffer (current-buffer)))
  226. (defun ein:notebook-open (url-or-port notebook-id &optional callback cbargs)
  227. "Open notebook of NOTEBOOK-ID in the server URL-OR-PORT.
  228. Opened notebook instance is returned. Note that notebook might not be
  229. ready at the time when this function is executed.
  230. After the notebook is opened, CALLBACK is called as::
  231. \(apply CALLBACK notebook CREATED CBARGS)
  232. where the second argument CREATED indicates whether the notebook
  233. is newly created or not. When CALLBACK is specified, buffer is
  234. **not** brought up by `pop-to-buffer'. It is caller's
  235. responsibility to do so. The current buffer is set to the
  236. notebook buffer when CALLBACK is called."
  237. (unless callback (setq callback #'ein:notebook-pop-to-current-buffer))
  238. (let ((buffer (ein:notebook-get-opened-buffer url-or-port notebook-id)))
  239. (if (buffer-live-p buffer)
  240. (with-current-buffer buffer
  241. (ein:log 'info "Notebook %s is already opened."
  242. (ein:$notebook-notebook-name ein:%notebook%))
  243. (when callback
  244. (apply callback ein:%notebook% nil cbargs))
  245. ein:%notebook%)
  246. (ein:log 'info "Opening notebook %s..." notebook-id)
  247. (ein:notebook-request-open url-or-port notebook-id callback cbargs))))
  248. (defun ein:notebook-request-open (url-or-port notebook-id
  249. &optional callback cbargs)
  250. "Request notebook of NOTEBOOK-ID to the server at URL-OR-PORT.
  251. Return `ein:$notebook' instance. Notebook may not be ready at
  252. the time of execution.
  253. CALLBACK is called as \(apply CALLBACK notebook t CBARGS). The second
  254. argument `t' indicates that the notebook is newly opened.
  255. See `ein:notebook-open' for more information."
  256. (let ((url (ein:notebook-url-from-url-and-id url-or-port notebook-id))
  257. (notebook (ein:notebook-new url-or-port notebook-id)))
  258. (ein:log 'debug "Opening notebook at %s" url)
  259. (ein:query-singleton-ajax
  260. (list 'notebook-open url-or-port notebook-id)
  261. url
  262. :timeout ein:notebook-querty-timeout-open
  263. :parser #'ein:json-read
  264. :success (apply-partially
  265. #'ein:notebook-request-open-callback-with-callback
  266. notebook callback cbargs))
  267. notebook))
  268. (defun ein:notebook-request-open-callback-with-callback (notebook
  269. callback
  270. cbargs
  271. &rest args)
  272. (apply #'ein:notebook-request-open-callback notebook args)
  273. (when callback
  274. (with-current-buffer (ein:notebook-buffer notebook)
  275. (apply callback notebook t cbargs))))
  276. (defun* ein:notebook-request-open-callback (notebook &key data
  277. &allow-other-keys)
  278. (let ((notebook-id (ein:$notebook-notebook-id notebook)))
  279. (ein:notebook-bind-events notebook (ein:events-new))
  280. (ein:notebook-start-kernel notebook)
  281. (ein:notebook-from-json notebook data) ; notebook buffer is created here
  282. (setf (ein:$notebook-kernelinfo notebook)
  283. (ein:kernelinfo-new (ein:$notebook-kernel notebook)
  284. (cons #'ein:notebook-buffer-list notebook)))
  285. (ein:notebook-put-opened-notebook notebook)
  286. (ein:notebook--check-nbformat data)
  287. (ein:log 'info "Notebook %s is ready"
  288. (ein:$notebook-notebook-name notebook))))
  289. (defun ein:notebook--different-number (n1 n2)
  290. (and (numberp n1) (numberp n2) (not (= n1 n2))))
  291. (defun ein:notebook--check-nbformat (data)
  292. "Warn user when nbformat is changed on server side.
  293. See https://github.com/ipython/ipython/pull/1934 for the purpose
  294. of minor mode."
  295. ;; See `Notebook.prototype.load_notebook_success'
  296. ;; at IPython/frontend/html/notebook/static/js/notebook.js
  297. (destructuring-bind (&key nbformat orig_nbformat
  298. nbformat_minor orig_nbformat_minor
  299. &allow-other-keys)
  300. data
  301. (cond
  302. ((ein:notebook--different-number nbformat orig_nbformat)
  303. (ein:display-warning
  304. (format "Notebook major version updated (v%d -> v%d).
  305. To not update version, do not save this notebook."
  306. orig_nbformat nbformat)))
  307. ((ein:notebook--different-number nbformat_minor orig_nbformat_minor)
  308. (ein:display-warning
  309. (format "This notebook is version v%s.%s, but IPython
  310. server you are using only fully support up to v%s.%s.
  311. Some features may not be available."
  312. orig_nbformat orig_nbformat_minor
  313. nbformat nbformat_minor))))))
  314. ;;; Initialization.
  315. (defun ein:notebook-bind-events (notebook events)
  316. "Bind events related to PAGER to the event handler EVENTS."
  317. (setf (ein:$notebook-events notebook) events)
  318. (ein:worksheet-class-bind-events events)
  319. ;; Bind events for sub components:
  320. (setf (ein:$notebook-pager notebook)
  321. (ein:pager-new
  322. (format ein:notebook-pager-buffer-name-template
  323. (ein:$notebook-url-or-port notebook)
  324. (ein:$notebook-notebook-name notebook))
  325. (ein:$notebook-events notebook))))
  326. (define-obsolete-function-alias
  327. 'ein:notebook-show-in-shared-output
  328. 'ein:shared-output-show-code-cell-at-point "0.1.2")
  329. ;;; Kernel related things
  330. (defun ein:notebook-start-kernel (notebook)
  331. (let* ((base-url (concat ein:base-kernel-url "kernels"))
  332. (kernel (ein:kernel-new (ein:$notebook-url-or-port notebook)
  333. base-url
  334. (ein:$notebook-events notebook))))
  335. (setf (ein:$notebook-kernel notebook) kernel)
  336. (ein:pytools-setup-hooks kernel)
  337. (ein:kernel-start kernel
  338. (ein:$notebook-notebook-id notebook))))
  339. (defun ein:notebook-restart-kernel (notebook)
  340. (ein:kernel-restart (ein:$notebook-kernel notebook)))
  341. (defun ein:notebook-restart-kernel-command ()
  342. "Send request to the server to restart kernel."
  343. (interactive)
  344. (if ein:%notebook%
  345. (when (y-or-n-p "Really restart kernel? ")
  346. (ein:notebook-restart-kernel ein:%notebook%))
  347. (ein:log 'error "Not in notebook buffer!")))
  348. (define-obsolete-function-alias
  349. 'ein:notebook-request-tool-tip-or-help-command
  350. 'ein:pytools-request-tooltip-or-help "0.1.2")
  351. (defun ein:notebook-complete-dot ()
  352. "Insert dot and request completion."
  353. (interactive)
  354. (if (and ein:%notebook% (ein:codecell-p (ein:get-cell-at-point)))
  355. (ein:completer-dot-complete)
  356. (insert ".")))
  357. (defun ein:notebook-kernel-interrupt-command ()
  358. "Interrupt the kernel.
  359. This is equivalent to do ``C-c`` in the console program."
  360. (interactive)
  361. (ein:kernel-interrupt (ein:$notebook-kernel ein:%notebook%)))
  362. (defun ein:notebook-kernel-kill-command ()
  363. (interactive)
  364. (when (y-or-n-p "Really kill kernel?")
  365. (ein:kernel-kill (ein:$notebook-kernel ein:%notebook%))))
  366. ;; autoexec
  367. (defun ein:notebook-execute-autoexec-cells (notebook)
  368. "Execute cells of which auto-execution flag is on."
  369. (interactive (list (or ein:%notebook% (error "Not in notebook buffer!"))))
  370. (mapc #'ein:worksheet-execute-autoexec-cells
  371. (ein:$notebook-worksheets notebook)))
  372. (define-obsolete-function-alias
  373. 'ein:notebook-eval-string
  374. 'ein:shared-output-eval-string "0.1.2")
  375. ;;; Persistence and loading
  376. (defun ein:notebook-set-notebook-name (notebook name)
  377. "Check NAME and change the name of NOTEBOOK to it."
  378. (if (ein:notebook-test-notebook-name name)
  379. (setf (ein:$notebook-notebook-name notebook) name)
  380. (ein:log 'error "%S is not a good notebook name." name)
  381. (error "%S is not a good notebook name." name)))
  382. (defun ein:notebook-test-notebook-name (name)
  383. (and (stringp name)
  384. (> (length name) 0)
  385. (not (string-match "[\\/\\\\:]" name))))
  386. (defun* ein:notebook--worksheet-new (notebook
  387. &optional (func #'ein:worksheet-new))
  388. (funcall func
  389. (ein:$notebook-nbformat notebook)
  390. (ein:notebook-name-getter notebook)
  391. (cons (lambda (notebook cell)
  392. (ein:notebook-discard-output-p notebook cell))
  393. notebook)
  394. (ein:$notebook-kernel notebook)
  395. (ein:$notebook-events notebook)))
  396. (defun ein:notebook--worksheet-render (notebook ws)
  397. (ein:worksheet-render ws)
  398. (with-current-buffer (ein:worksheet-buffer ws)
  399. (ein:notebook-mode)
  400. ;; Now that major-mode is set, set buffer local variables:
  401. (ein:notebook--notification-setup notebook)
  402. (ein:notebook-setup-kill-buffer-hook)
  403. (ein:notebook-set-buffer-file-name-maybe notebook)
  404. (setq ein:%notebook% notebook)))
  405. (defun ein:notebook--notification-setup (notebook)
  406. (ein:notification-setup
  407. (current-buffer)
  408. (ein:$notebook-events notebook)
  409. :get-list
  410. (lambda () (ein:$notebook-worksheets ein:%notebook%))
  411. :get-current
  412. (lambda () ein:%worksheet%)
  413. :get-name
  414. #'ein:worksheet-name
  415. :get-buffer
  416. (lambda (ws)
  417. (ein:notebook-worksheet--render-maybe ein:%notebook% ws "clicked")
  418. (ein:worksheet-buffer ws))
  419. :delete
  420. (lambda (ws)
  421. (ein:notebook-worksheet-delete ein:%notebook% ws t))
  422. :insert-prev
  423. (lambda (ws) (ein:notebook-worksheet-insert-prev ein:%notebook% ws))
  424. :insert-next
  425. (lambda (ws) (ein:notebook-worksheet-insert-next ein:%notebook% ws))
  426. :move-prev
  427. (lambda (ws) (ein:notebook-worksheet-move-prev ein:%notebook% ws))
  428. :move-next
  429. (lambda (ws) (ein:notebook-worksheet-move-next ein:%notebook% ws))
  430. ))
  431. (defun ein:notebook-set-buffer-file-name-maybe (notebook)
  432. "Set `buffer-file-name' of the current buffer to ipynb file
  433. of NOTEBOOK."
  434. (when ein:notebook-set-buffer-file-name
  435. (ein:notebook-fetch-data
  436. notebook
  437. (lambda (data notebook buffer)
  438. (with-current-buffer buffer
  439. (destructuring-bind (&key project &allow-other-keys)
  440. data
  441. (setq buffer-file-name
  442. (expand-file-name
  443. (format "%s.ipynb"
  444. (ein:$notebook-notebook-name notebook))
  445. project)))))
  446. (list notebook (current-buffer)))))
  447. (defun ein:notebook-from-json (notebook data)
  448. (destructuring-bind (&key metadata nbformat nbformat_minor
  449. &allow-other-keys)
  450. data
  451. (setf (ein:$notebook-metadata notebook) metadata)
  452. (setf (ein:$notebook-nbformat notebook) nbformat)
  453. (setf (ein:$notebook-nbformat-minor notebook) nbformat_minor)
  454. (setf (ein:$notebook-notebook-name notebook) (plist-get metadata :name)))
  455. (setf (ein:$notebook-worksheets notebook)
  456. (mapcar (lambda (ws-data)
  457. (ein:worksheet-from-json
  458. (ein:notebook--worksheet-new notebook) ws-data))
  459. (or (plist-get data :worksheets)
  460. (list nil))))
  461. (ein:notebook--worksheet-render notebook
  462. (nth 0 (ein:$notebook-worksheets notebook)))
  463. notebook)
  464. (defun ein:notebook-to-json (notebook)
  465. "Return json-ready alist."
  466. (let ((worksheets (mapcar #'ein:worksheet-to-json
  467. (ein:$notebook-worksheets notebook))))
  468. `((worksheets . ,(apply #'vector worksheets))
  469. (metadata . ,(ein:$notebook-metadata notebook)))))
  470. (defun ein:notebook-save-notebook (notebook retry &optional callback cbarg)
  471. (let ((data (ein:notebook-to-json notebook)))
  472. (plist-put (cdr (assq 'metadata data))
  473. :name (ein:$notebook-notebook-name notebook))
  474. (push `(nbformat . ,(ein:$notebook-nbformat notebook)) data)
  475. (ein:aif (ein:$notebook-nbformat-minor notebook)
  476. ;; Do not set nbformat when it is not given from server.
  477. (push `(nbformat_minor . ,it) data))
  478. (ein:events-trigger (ein:$notebook-events notebook)
  479. 'notebook_saving.Notebook)
  480. (ein:query-singleton-ajax
  481. (list 'notebook-save
  482. (ein:$notebook-url-or-port notebook)
  483. (ein:$notebook-notebook-id notebook))
  484. (ein:notebook-url notebook)
  485. :timeout ein:notebook-querty-timeout-save
  486. :type "PUT"
  487. :headers '(("Content-Type" . "application/json"))
  488. :data (json-encode data)
  489. :error (apply-partially #'ein:notebook-save-notebook-error notebook)
  490. :success (apply-partially #'ein:notebook-save-notebook-workaround
  491. notebook retry callback cbarg)
  492. :status-code
  493. `((204 . ,(apply-partially
  494. (lambda (notebook callback cbarg &rest rest)
  495. (apply #'ein:notebook-save-notebook-success
  496. notebook rest)
  497. (when callback
  498. (apply callback cbarg rest)))
  499. notebook callback cbarg))))))
  500. (defun ein:notebook-save-notebook-command ()
  501. "Save the notebook."
  502. (interactive)
  503. (ein:notebook-save-notebook ein:%notebook% 0))
  504. (defun* ein:notebook-save-notebook-workaround
  505. (notebook retry callback cbarg
  506. &key
  507. status
  508. response
  509. &allow-other-keys
  510. &aux
  511. (response-status (request-response-status-code response)))
  512. ;; IPython server returns 204 only when the notebook URL is
  513. ;; accessed via PUT or DELETE. As it seems Emacs failed to
  514. ;; choose PUT method every two times, let's check the response
  515. ;; here and fail when 204 is not returned.
  516. (unless (eq response-status 204)
  517. (with-current-buffer (ein:notebook-buffer notebook)
  518. (if (< retry ein:notebook-save-retry-max)
  519. (progn
  520. (ein:log 'info "Retry saving... Next count: %s" (1+ retry))
  521. (ein:notebook-save-notebook notebook (1+ retry)
  522. callback cbarg))
  523. (ein:notebook-save-notebook-error notebook :status status)
  524. (ein:log 'info
  525. "Status code (=%s) is not 204 and retry exceeds limit (=%s)."
  526. response-status ein:notebook-save-retry-max)))))
  527. (defun ein:notebook-save-notebook-success (notebook &rest ignore)
  528. (ein:log 'info "Notebook is saved.")
  529. (setf (ein:$notebook-dirty notebook) nil)
  530. (mapc (lambda (ws)
  531. (ein:worksheet-save-cells ws) ; [#]_
  532. (ein:worksheet-set-modified-p ws nil))
  533. (ein:$notebook-worksheets notebook))
  534. (ein:events-trigger (ein:$notebook-events notebook)
  535. 'notebook_saved.Notebook))
  536. ;; .. [#] Consider the following case.
  537. ;; (1) Open worksheet WS0 and other worksheets.
  538. ;; (2) Edit worksheet WS0 then save the notebook.
  539. ;; (3) Edit worksheet WS0.
  540. ;; (4) Kill WS0 buffer by discarding the edit.
  541. ;; (5) Save the notebook.
  542. ;; This should save the latest WS0. To do so, WS0 at the point (2)
  543. ;; must be cached in the worksheet slot `:saved-cells'.
  544. (defun* ein:notebook-save-notebook-error (notebook &key symbol-status
  545. &allow-other-keys)
  546. (if (eq symbol-status 'user-cancel)
  547. (ein:log 'info "Cancel saving notebook.")
  548. (ein:log 'info "Failed to save notebook!")
  549. (ein:events-trigger (ein:$notebook-events notebook)
  550. 'notebook_save_failed.Notebook)))
  551. (defun ein:notebook-rename-command (name)
  552. "Rename current notebook and save it immediately.
  553. NAME is any non-empty string that does not contain '/' or '\\'."
  554. (interactive
  555. (list (read-string "Rename notebook: "
  556. (let ((name (ein:$notebook-notebook-name ein:%notebook%)))
  557. (unless (string-match "Untitled[0-9]+" name)
  558. name)))))
  559. (ein:notebook-set-notebook-name ein:%notebook% name)
  560. (mapc #'ein:worksheet-set-buffer-name
  561. (ein:$notebook-worksheets ein:%notebook%))
  562. (ein:notebook-save-notebook
  563. ein:%notebook% 0
  564. (lambda (notebook &rest ignore)
  565. (with-current-buffer (ein:notebook-buffer notebook)
  566. (run-hooks 'ein:notebook-after-rename-hook)))
  567. ein:%notebook%))
  568. (defun ein:notebook-close (notebook)
  569. "Close NOTEBOOK and kill its buffer."
  570. (interactive (prog1 (list (ein:notebook--get-nb-or-error))
  571. (or (ein:notebook-ask-before-kill-buffer)
  572. (error "Quit"))))
  573. (let ((ein:notebook-kill-buffer-ask nil))
  574. ;; Let `ein:notebook-kill-buffer-callback' do its job.
  575. (mapc #'kill-buffer (ein:notebook-buffer-list notebook))))
  576. (defun ein:notebook-kill-kernel-then-close-command ()
  577. "Kill kernel and then kill notebook buffer.
  578. To close notebook without killing kernel, just close the buffer
  579. as usual."
  580. (interactive)
  581. (when (ein:notebook-ask-before-kill-buffer)
  582. (let ((kernel (ein:$notebook-kernel ein:%notebook%)))
  583. ;; If kernel is live, kill it before closing.
  584. (if (ein:kernel-live-p kernel)
  585. (ein:kernel-kill kernel #'ein:notebook-close (list ein:%notebook%))
  586. (ein:notebook-close ein:%notebook%)))))
  587. ;;; Worksheet
  588. (defmacro ein:notebook--worksheet-render-new (notebook type)
  589. "Create new worksheet of TYPE in NOTEBOOK."
  590. (let ((func (intern (format "ein:%s-new" type)))
  591. (slot (list (intern (format "ein:$notebook-%ss" type)) notebook)))
  592. `(let ((ws (ein:notebook--worksheet-new ,notebook #',func)))
  593. (setf ,slot (append ,slot (list ws)))
  594. (ein:notebook--worksheet-render ,notebook ws)
  595. ws)))
  596. (defun ein:notebook-worksheet-render-new (notebook)
  597. "Create new worksheet in NOTEBOOK."
  598. (ein:notebook--worksheet-render-new notebook worksheet))
  599. (defun ein:notebook-worksheet-open-next-or-new (notebook ws &optional show)
  600. "Open next worksheet. Create new if none.
  601. Try to open the worksheet to the worksheet WS using the function
  602. `ein:notebook-worksheet-open-next', open a new worksheet if not
  603. found.
  604. SHOW is a function to be called with the worksheet buffer if
  605. given."
  606. (interactive (list (ein:notebook--get-nb-or-error)
  607. (ein:worksheet--get-ws-or-error)
  608. #'switch-to-buffer))
  609. (let ((next (ein:notebook-worksheet-open-next notebook ws)))
  610. (unless next
  611. (ein:log 'info "Creating new worksheet...")
  612. (setq next (ein:notebook-worksheet-render-new notebook))
  613. (ein:log 'info "Creating new worksheet... Done."))
  614. (when show
  615. (funcall show (ein:worksheet-buffer next)))))
  616. (defun ein:notebook-worksheet-open-next-or-first (notebook ws &optional show)
  617. "Open next or first worksheet.
  618. Try to open the worksheet to the worksheet WS using the function
  619. `ein:notebook-worksheet-open-next', open the first worksheet if
  620. not found.
  621. SHOW is a function to be called with the worksheet buffer if
  622. given."
  623. (interactive (list (ein:notebook--get-nb-or-error)
  624. (ein:worksheet--get-ws-or-error)
  625. #'switch-to-buffer))
  626. (let ((next (ein:notebook-worksheet-open-next notebook ws)))
  627. (unless next
  628. (setq next (car (ein:$notebook-worksheets notebook))))
  629. (when show
  630. (funcall show (ein:worksheet-buffer next)))))
  631. (defun ein:notebook-worksheet-open-prev-or-last (notebook ws &optional show)
  632. "Open previous or last worksheet.
  633. See also `ein:notebook-worksheet-open-next-or-first' and
  634. `ein:notebook-worksheet-open-prev'."
  635. (interactive (list (ein:notebook--get-nb-or-error)
  636. (ein:worksheet--get-ws-or-error)
  637. #'switch-to-buffer))
  638. (let ((prev (ein:notebook-worksheet-open-prev notebook ws)))
  639. (unless prev
  640. (setq prev (car (last (ein:$notebook-worksheets notebook)))))
  641. (when show
  642. (funcall show (ein:worksheet-buffer prev)))))
  643. (defun* ein:notebook-worksheet--render-maybe
  644. (notebook ws &optional (adj "next"))
  645. "Render worksheet WS of NOTEBOOK if it does not have buffer.
  646. ADJ is a adjective to describe worksheet to be rendered."
  647. (if (ein:worksheet-has-buffer-p ws)
  648. (ein:log 'verbose "The worksheet already has a buffer.")
  649. (ein:log 'info "Rendering %s worksheet..." adj)
  650. (ein:notebook--worksheet-render notebook ws)
  651. (ein:log 'info "Rendering %s worksheet... Done." adj)))
  652. (defun* ein:notebook-worksheet--open-new
  653. (notebook new &optional (adj "next") show)
  654. "Open (possibly new) worksheet NEW of NOTEBOOK with SHOW function.
  655. ADJ is a adjective to describe worksheet to be opened.
  656. SHOW is a function to be called with worksheet buffer if given."
  657. (when new
  658. (ein:notebook-worksheet--render-maybe notebook new adj))
  659. (when show
  660. (assert (ein:worksheet-p new) nil "No %s worksheet." adj)
  661. (funcall show (ein:worksheet-buffer new))))
  662. (defun ein:notebook-worksheet-open-next (notebook ws &optional show)
  663. "Open next worksheet.
  664. Search the worksheet after the worksheet WS, render it if it is
  665. not yet, then return the worksheet. If there is no such
  666. worksheet, return nil. Open the first worksheet if the worksheet
  667. WS is an instance of `ein:scratchsheet'.
  668. SHOW is a function to be called with the worksheet buffer if
  669. given."
  670. (interactive (list (ein:notebook--get-nb-or-error)
  671. (ein:worksheet--get-ws-or-error)
  672. #'switch-to-buffer))
  673. (let ((next (if (ein:scratchsheet-p ws)
  674. (car (ein:$notebook-worksheets notebook))
  675. (loop with worksheets = (ein:$notebook-worksheets notebook)
  676. for current in worksheets
  677. for next in (cdr worksheets)
  678. when (eq current ws) return next))))
  679. (ein:notebook-worksheet--open-new notebook next "next" show)
  680. next))
  681. (defun ein:notebook-worksheet-open-prev (notebook ws &optional show)
  682. "Open previous worksheet.
  683. See also `ein:notebook-worksheet-open-next'."
  684. (interactive (list (ein:notebook--get-nb-or-error)
  685. (ein:worksheet--get-ws-or-error)
  686. #'switch-to-buffer))
  687. (let ((prev (if (ein:scratchsheet-p ws)
  688. (car (last (ein:$notebook-worksheets notebook)))
  689. (loop for (prev current) on (ein:$notebook-worksheets notebook)
  690. when (eq current ws) return prev))))
  691. (ein:notebook-worksheet--open-new notebook prev "previous" show)
  692. prev))
  693. (defun ein:notebook-worksheet-open-ith (notebook i &optional show)
  694. "Open I-th (zero-origin) worksheet."
  695. (let ((ws (nth i (ein:$notebook-worksheets notebook))))
  696. (unless ws (error "No %s-th worksheet" (1+ i)))
  697. (ein:notebook-worksheet--open-new notebook ws (format "%s-th" i) show)))
  698. (defmacro ein:notebook-worksheet--defun-open-nth (n)
  699. "Define a command to open N-th (one-origin) worksheet."
  700. (assert (and (integerp n) (> n 0)) t)
  701. (let ((func (intern (format "ein:notebook-worksheet-open-%sth" n))))
  702. `(defun ,func (notebook &optional show)
  703. ,(format "Open %d-th worksheet." n)
  704. (interactive (list (ein:notebook--get-nb-or-error)
  705. #'switch-to-buffer))
  706. (ein:notebook-worksheet-open-ith notebook ,(1- n) show))))
  707. (defmacro ein:notebook-worksheet--defun-all-open-nth (min max)
  708. `(progn
  709. ,@(loop for n from min to max
  710. collect `(ein:notebook-worksheet--defun-open-nth ,n))))
  711. (ein:notebook-worksheet--defun-all-open-nth 1 8)
  712. (defun ein:notebook-worksheet-open-last (notebook &optional show)
  713. "Open the last worksheet."
  714. (interactive (list (ein:notebook--get-nb-or-error)
  715. #'switch-to-buffer))
  716. (let ((last (car (last (ein:$notebook-worksheets notebook)))))
  717. (ein:notebook-worksheet--open-new notebook last "last" show)
  718. last))
  719. (defun ein:notebook-worksheet-insert-new (notebook ws &optional render show
  720. inserter)
  721. (let ((new (ein:notebook--worksheet-new notebook)))
  722. (setf (ein:$notebook-worksheets notebook)
  723. (funcall inserter (ein:$notebook-worksheets notebook) ws new))
  724. (when (or render show)
  725. (ein:notebook--worksheet-render notebook new))
  726. (when show
  727. (funcall show (ein:worksheet-buffer new)))
  728. new))
  729. (defun* ein:notebook-worksheet-insert-next
  730. (notebook ws &optional (render t) (show #'switch-to-buffer))
  731. "Insert a new worksheet after this worksheet and open it.
  732. See also `ein:notebook-worksheet-insert-prev'.
  733. .. The worksheet WS is searched in the worksheets slot of
  734. NOTEBOOK and a newly created worksheet is inserted after WS.
  735. Worksheet buffer is created when RENDER or SHOW is non-`nil'.
  736. SHOW is a function which take a buffer."
  737. (interactive (list (ein:notebook--get-nb-or-error)
  738. (ein:worksheet--get-ws-or-error)))
  739. (ein:notebook-worksheet-insert-new notebook ws render show
  740. #'ein:list-insert-after))
  741. (defun* ein:notebook-worksheet-insert-prev
  742. (notebook ws &optional (render t) (show #'switch-to-buffer))
  743. "Insert a new worksheet before this worksheet and open it.
  744. See also `ein:notebook-worksheet-insert-next'."
  745. (interactive (list (ein:notebook--get-nb-or-error)
  746. (ein:worksheet--get-ws-or-error)))
  747. (ein:notebook-worksheet-insert-new notebook ws render show
  748. #'ein:list-insert-before))
  749. (defun ein:notebook-worksheet-delete (notebook ws &optional confirm)
  750. "Delete the current worksheet.
  751. When used as a lisp function, delete worksheet WS from NOTEBOOk."
  752. (interactive (list (ein:notebook--get-nb-or-error)
  753. (ein:worksheet--get-ws-or-error)
  754. t))
  755. (when confirm
  756. (unless (y-or-n-p
  757. "Really remove this worksheet? There is no undo.")
  758. (error "Quit deleting the current worksheet.")))
  759. (setf (ein:$notebook-worksheets notebook)
  760. (delq ws (ein:$notebook-worksheets notebook)))
  761. (setf (ein:$notebook-dirty notebook) t)
  762. (let ((ein:notebook-kill-buffer-ask nil))
  763. (kill-buffer (ein:worksheet-buffer ws))))
  764. (defun ein:notebook-worksheet-move-prev (notebook ws)
  765. "Switch the current worksheet with the previous one."
  766. (interactive (list (ein:notebook--get-nb-or-error)
  767. (ein:worksheet--get-ws-or-error)))
  768. (assert (ein:worksheet-p ws) nil "Not worksheet.")
  769. (setf (ein:$notebook-worksheets notebook)
  770. (ein:list-move-left (ein:$notebook-worksheets notebook) ws)))
  771. (defun ein:notebook-worksheet-move-next (notebook ws)
  772. "Switch the current worksheet with the previous one."
  773. (interactive (list (ein:notebook--get-nb-or-error)
  774. (ein:worksheet--get-ws-or-error)))
  775. (assert (ein:worksheet-p ws) nil "Not worksheet.")
  776. (setf (ein:$notebook-worksheets notebook)
  777. (ein:list-move-right (ein:$notebook-worksheets notebook) ws)))
  778. (defun* ein:notebook-worksheet-index
  779. (&optional (notebook ein:%notebook%)
  780. (ws ein:%worksheet%))
  781. "Return an index of the worksheet WS in NOTEBOOK."
  782. (loop for i from 0
  783. for ith-ws in (ein:$notebook-worksheets notebook)
  784. when (eq ith-ws ws)
  785. return i))
  786. ;;; Scratch sheet
  787. (defun ein:notebook-scratchsheet-render-new (notebook)
  788. "Create new scratchsheet in NOTEBOOK."
  789. (ein:notebook--worksheet-render-new notebook scratchsheet))
  790. (defun ein:notebook-scratchsheet-open (notebook &optional new popup)
  791. "Open \"scratch sheet\".
  792. Open a new one when prefix argument is given.
  793. Scratch sheet is almost identical to worksheet. However, EIN
  794. will not save the buffer. Use this buffer like of normal IPython
  795. console. Note that you can always copy cells into the normal
  796. worksheet to save result."
  797. (interactive (list (ein:get-notebook-or-error)
  798. current-prefix-arg
  799. t))
  800. (let ((ss (or (unless new
  801. (car (ein:$notebook-scratchsheets notebook)))
  802. (ein:notebook-scratchsheet-render-new notebook))))
  803. (when popup
  804. (pop-to-buffer (ein:worksheet-buffer ss)))
  805. ss))
  806. ;;; Opened notebooks
  807. (defvar ein:notebook--opened-map (make-hash-table :test 'equal)
  808. "A map: (URL-OR-PORT NOTEBOOK-ID) => notebook instance.")
  809. (defun ein:notebook-get-opened-notebook (url-or-port notebook-id)
  810. (gethash (list url-or-port notebook-id) ein:notebook--opened-map))
  811. (defun ein:notebook-get-opened-buffer (url-or-port notebook-id)
  812. (ein:aand (ein:notebook-get-opened-notebook url-or-port notebook-id)
  813. (ein:notebook-buffer it)))
  814. (defun ein:notebook-put-opened-notebook (notebook)
  815. (puthash (list (ein:$notebook-url-or-port notebook)
  816. (ein:$notebook-notebook-id notebook))
  817. notebook
  818. ein:notebook--opened-map))
  819. (defun ein:notebook-opened-notebooks (&optional predicate)
  820. "Return list of opened notebook instances.
  821. If PREDICATE is given, notebooks are filtered by PREDICATE.
  822. PREDICATE is called with each notebook and notebook is included
  823. in the returned list only when PREDICATE returns non-nil value."
  824. (let (notebooks)
  825. (maphash (lambda (k n) (if (ein:notebook-live-p n)
  826. (push n notebooks)
  827. (remhash k ein:notebook--opened-map)))
  828. ein:notebook--opened-map)
  829. (if predicate
  830. (ein:filter predicate notebooks)
  831. notebooks)))
  832. (defun ein:notebook-opened-buffers (&optional predicate)
  833. "Return list of opened notebook buffers."
  834. (mapcar #'ein:notebook-buffer (ein:notebook-opened-notebooks predicate)))
  835. (defun ein:notebook-opened-buffer-names (&optional predicate)
  836. "Return list of opened notebook buffer names."
  837. (mapcar #'buffer-name (ein:notebook-opened-buffers predicate)))
  838. ;;; Generic getter
  839. (defun ein:get-url-or-port--notebook ()
  840. (when ein:%notebook% (ein:$notebook-url-or-port ein:%notebook%)))
  841. (defun ein:get-notebook--notebook ()
  842. ein:%notebook%)
  843. (defun ein:get-kernel--notebook ()
  844. (when (ein:$notebook-p ein:%notebook%)
  845. (ein:$notebook-kernel ein:%notebook%)))
  846. ;;; Predicate
  847. (defun ein:notebook-buffer-p ()
  848. "Return non-`nil' if current buffer is notebook buffer."
  849. ein:%notebook%)
  850. (defun ein:notebook-live-p (notebook)
  851. "Return non-`nil' if NOTEBOOK has live buffer."
  852. (buffer-live-p (ein:notebook-buffer notebook)))
  853. (defun ein:notebook-modified-p (&optional notebook)
  854. "Return non-nil if NOTEBOOK is modified.
  855. If NOTEBOOK is not given or nil then consider the notebook
  856. associated with current buffer (if any)."
  857. (unless notebook (setq notebook ein:%notebook%))
  858. (and (ein:$notebook-p notebook)
  859. (ein:notebook-live-p notebook)
  860. (or (ein:$notebook-dirty notebook)
  861. (loop for ws in (ein:$notebook-worksheets notebook)
  862. when (ein:worksheet-modified-p ws)
  863. return t))))
  864. ;;; Notebook mode
  865. (defcustom ein:notebook-modes
  866. '(ein:notebook-multilang-mode)
  867. "Notebook modes to use \(in order of preference).
  868. When the notebook is opened, mode in this value is checked one by one
  869. and the first usable mode is used.
  870. Available modes:
  871. * `ein:notebook-multilang-mode'
  872. * `ein:notebook-mumamo-mode'
  873. * `ein:notebook-python-mode'
  874. * `ein:notebook-plain-mode'
  875. Examples:
  876. Use MuMaMo if it is installed. Otherwise, use plain mode.
  877. This is the old default setting::
  878. (setq ein:notebook-modes '(ein:notebook-mumamo-mode ein:notebook-plain-mode))
  879. Avoid using MuMaMo even when it is installed::
  880. (setq ein:notebook-modes '(ein:notebook-plain-mode))
  881. Use simple `python-mode' based notebook mode when MuMaMo is not installed::
  882. (setq ein:notebook-modes '(ein:notebook-mumamo-mode ein:notebook-python-mode))
  883. "
  884. :type '(repeat (choice (const :tag "Multi-lang" ein:notebook-multilang-mode)
  885. (const :tag "MuMaMo" ein:notebook-mumamo-mode)
  886. (const :tag "Only Python" ein:notebook-python-mode)
  887. (const :tag "Plain" ein:notebook-plain-mode)))
  888. :group 'ein)
  889. (defcustom ein:notebook-mode-hook nil
  890. "Hook for `ein:notebook-mode'.
  891. This hook is run regardless the actual major mode used."
  892. :type 'hook
  893. :group 'ein)
  894. (defun ein:notebook-choose-mode ()
  895. "Return usable (defined) notebook mode."
  896. ;; So try to load extra modules here.
  897. (when (require 'mumamo nil t)
  898. (require 'ein-mumamo))
  899. ;; Return first matched mode
  900. (loop for mode in ein:notebook-modes
  901. if (functionp mode)
  902. return mode))
  903. (defvar ein:notebook-mode-map (make-sparse-keymap))
  904. (let ((map ein:notebook-mode-map))
  905. (define-key map "\C-c\C-c" 'ein:worksheet-execute-cell)
  906. (define-key map (kbd "M-RET") 'ein:worksheet-execute-cell-and-goto-next)
  907. (define-key map (kbd "<M-S-return>")
  908. 'ein:worksheet-execute-cell-and-insert-below)
  909. (define-key map (kbd "C-c C-'") 'ein:worksheet-turn-on-autoexec)
  910. (define-key map "\C-c\C-e" 'ein:worksheet-toggle-output)
  911. (define-key map "\C-c\C-v" 'ein:worksheet-set-output-visibility-all)
  912. (define-key map "\C-c\C-l" 'ein:worksheet-clear-output)
  913. (define-key map (kbd "C-c C-S-l") 'ein:worksheet-clear-all-output)
  914. (define-key map (kbd "C-c C-;") 'ein:shared-output-show-code-cell-at-point)
  915. (define-key map "\C-c\C-k" 'ein:worksheet-kill-cell)
  916. (define-key map "\C-c\M-w" 'ein:worksheet-copy-cell)
  917. (define-key map "\C-c\C-w" 'ein:worksheet-copy-cell)
  918. (define-key map "\C-c\C-y" 'ein:worksheet-yank-cell)
  919. (define-key map "\C-c\C-a" 'ein:worksheet-insert-cell-above)
  920. (define-key map "\C-c\C-b" 'ein:worksheet-insert-cell-below)
  921. (define-key map "\C-c\C-t" 'ein:worksheet-toggle-cell-type)
  922. (define-key map "\C-c\C-u" 'ein:worksheet-change-cell-type)
  923. (define-key map "\C-c\C-s" 'ein:worksheet-split-cell-at-point)
  924. (define-key map "\C-c\C-m" 'ein:worksheet-merge-cell)
  925. (define-key map "\C-c\C-n" 'ein:worksheet-goto-next-input)
  926. (define-key map "\C-c\C-p" 'ein:worksheet-goto-prev-input)
  927. (define-key map (kbd "C-<up>") 'ein:worksheet-goto-prev-input)
  928. (define-key map (kbd "C-<down>") 'ein:worksheet-goto-next-input)
  929. (define-key map (kbd "C-c <up>") 'ein:worksheet-move-cell-up)
  930. (define-key map (kbd "C-c <down>") 'ein:worksheet-move-cell-down)
  931. (define-key map (kbd "M-<up>") 'ein:worksheet-move-cell-up)
  932. (define-key map (kbd "M-<down>") 'ein:worksheet-move-cell-down)
  933. (define-key map "\C-c\C-f" 'ein:pytools-request-tooltip-or-help)
  934. (define-key map "\C-c\C-i" 'ein:completer-complete)
  935. (define-key map "\C-c\C-x" 'ein:tb-show)
  936. (define-key map "\C-c\C-r" 'ein:notebook-restart-kernel-command)
  937. (define-key map "\C-c\C-z" 'ein:notebook-kernel-interrupt-command)
  938. (define-key map "\C-c\C-q" 'ein:notebook-kill-kernel-then-close-command)
  939. (define-key map (kbd "C-c C-#") 'ein:notebook-close)
  940. (define-key map (kbd "C-:") 'ein:shared-output-eval-string)
  941. (define-key map "\C-c\C-o" 'ein:console-open)
  942. (define-key map "\C-x\C-s" 'ein:notebook-save-notebook-command)
  943. (define-key map "\C-x\C-w" 'ein:notebook-rename-command)
  944. (define-key map "\M-." 'ein:pytools-jump-to-source-command)
  945. (define-key map (kbd "C-c C-.") 'ein:pytools-jump-to-source-command)
  946. (define-key map "\M-," 'ein:pytools-jump-back-command)
  947. (define-key map (kbd "C-c C-,") 'ein:pytools-jump-back-command)
  948. (define-key map "\M-p" 'ein:worksheet-previous-input-history)
  949. (define-key map "\M-n" 'ein:worksheet-next-input-history)
  950. (define-key map (kbd "C-c C-/") 'ein:notebook-scratchsheet-open)
  951. ;; Worksheets
  952. (define-key map (kbd "C-c !") 'ein:worksheet-rename-sheet)
  953. (define-key map (kbd "C-c {") 'ein:notebook-worksheet-open-prev-or-last)
  954. (define-key map (kbd "C-c }") 'ein:notebook-worksheet-open-next-or-first)
  955. (define-key map (kbd "C-c M-{") 'ein:notebook-worksheet-move-prev)
  956. (define-key map (kbd "C-c M-}") 'ein:notebook-worksheet-move-next)
  957. (define-key map (kbd "C-c +") 'ein:notebook-worksheet-insert-next)
  958. (define-key map (kbd "C-c M-+") 'ein:notebook-worksheet-insert-prev)
  959. (define-key map (kbd "C-c -") 'ein:notebook-worksheet-delete)
  960. (loop for n from 1 to 8
  961. do (define-key map (format "\C-c%d" n)
  962. (intern (format "ein:notebook-worksheet-open-%sth" n))))
  963. (define-key map "\C-c9" 'ein:notebook-worksheet-open-last)
  964. ;; Menu
  965. (easy-menu-define ein:notebook-menu map "EIN Notebook Mode Menu"
  966. `("EIN Notebook"
  967. ("File"
  968. ,@(ein:generate-menu
  969. '(("Save notebook" ein:notebook-save-notebook-command)
  970. ("Rename notebook" ein:notebook-rename-command)
  971. ("Close notebook without saving"
  972. ein:notebook-close)
  973. ("Kill kernel then close notebook"
  974. ein:notebook-kill-kernel-then-close-command))))
  975. ("Edit"
  976. ,@(ein:generate-menu
  977. '(("Kill cell" ein:worksheet-kill-cell)
  978. ("Copy cell" ein:worksheet-copy-cell)
  979. ("Yank cell" ein:worksheet-yank-cell)
  980. ("Insert cell above" ein:worksheet-insert-cell-above)
  981. ("Insert cell below" ein:worksheet-insert-cell-below)
  982. ("Toggle cell type" ein:worksheet-toggle-cell-type)
  983. ("Change cell type" ein:worksheet-change-cell-type)
  984. ("Split cell at point" ein:worksheet-split-cell-at-point)
  985. ("Merge cell" ein:worksheet-merge-cell)
  986. ("Go to next cell" ein:worksheet-goto-next-input)
  987. ("Go to previous cell" ein:worksheet-goto-prev-input)
  988. ("Move cell up" ein:worksheet-move-cell-up)
  989. ("Move cell down" ein:worksheet-move-cell-down)
  990. ("Dedent text in CELL" ein:worksheet-dedent-cell-text)
  991. )))
  992. ("Cell/Code"
  993. ,@(ein:generate-menu
  994. '(("Execute cell" ein:worksheet-execute-cell
  995. :active (ein:worksheet-at-codecell-p))
  996. ("Execute cell and go to next"
  997. ein:worksheet-execute-cell-and-goto-next
  998. :active (ein:worksheet-at-codecell-p))
  999. ("Execute cell and insert below"
  1000. ein:worksheet-execute-cell-and-insert-below
  1001. :active (ein:worksheet-at-codecell-p))
  1002. ("Execute all"
  1003. ein:worksheet-execute-all-cell)
  1004. ("Turn on auto execution flag" ein:worksheet-turn-on-autoexec
  1005. :active (ein:worksheet-at-codecell-p))
  1006. ("Evaluate code in minibuffer" ein:shared-output-eval-string)
  1007. ("Toggle instant cell execution mode" ein:iexec-mode)
  1008. ))
  1009. "---"
  1010. ,@(ein:generate-menu
  1011. '(("Toggle output visibility" ein:worksheet-toggle-output
  1012. :active (ein:worksheet-at-codecell-p))
  1013. ("Show all output"
  1014. ein:worksheet-set-output-visibility-all)
  1015. ("Discard output" ein:worksheet-clear-output
  1016. :active (ein:worksheet-at-codecell-p))
  1017. ("Discard all output" ein:worksheet-clear-all-output)
  1018. ("Show full output" ein:shared-output-show-code-cell-at-point
  1019. :active (ein:worksheet-at-codecell-p))
  1020. ("Traceback viewer" ein:tb-show)
  1021. ))
  1022. "---"
  1023. ,@(ein:generate-menu
  1024. '(("Show object help"
  1025. ein:pytools-request-tooltip-or-help)
  1026. ("Complete code" ein:completer-complete
  1027. :active (ein:worksheet-at-codecell-p))
  1028. ("Jump to definition" ein:pytools-jump-to-source-command)
  1029. ("Go back to the previous jump point"
  1030. ein:pytools-jump-back-command)
  1031. ("Previous input history"
  1032. ein:worksheet-previous-input-history)
  1033. ("Next input history"
  1034. ein:worksheet-next-input-history))))
  1035. ("Kernel"
  1036. ,@(ein:generate-menu
  1037. '(("Restart kernel" ein:notebook-restart-kernel-command)
  1038. ("Interrupt kernel" ein:notebook-kernel-interrupt-command))))
  1039. ("Worksheets [Experimental]"
  1040. ,@(ein:generate-menu
  1041. '(("Rename worksheet" ein:worksheet-rename-sheet)
  1042. ("Insert next worksheet"
  1043. ein:notebook-worksheet-insert-next)
  1044. ("Insert previous worksheet"
  1045. ein:notebook-worksheet-insert-prev)
  1046. ("Delete worksheet" ein:notebook-worksheet-delete)
  1047. ("Move worksheet left" ein:notebook-worksheet-move-prev)
  1048. ("Move worksheet right" ein:notebook-worksheet-move-next)
  1049. ))
  1050. "---"
  1051. ,@(ein:generate-menu
  1052. '(("Open previous worksheet"
  1053. ein:notebook-worksheet-open-prev)
  1054. ("Open previous or last worksheet"
  1055. ein:notebook-worksheet-open-prev-or-last)
  1056. ("Open next worksheet"
  1057. ein:notebook-worksheet-open-next)
  1058. ("Open next or first worksheet"
  1059. ein:notebook-worksheet-open-next-or-first)
  1060. ("Open next or new worksheet"
  1061. ein:notebook-worksheet-open-next-or-new)
  1062. ))
  1063. "---"
  1064. ,@(ein:generate-menu
  1065. (append
  1066. (loop for n from 1 to 8
  1067. collect
  1068. (list
  1069. (format "Open %d-th worksheet" n)
  1070. (intern (format "ein:notebook-worksheet-open-%sth" n))))
  1071. '(("Open last worksheet" ein:notebook-worksheet-open-last)))))
  1072. ("Junk notebook"
  1073. ,@(ein:generate-menu
  1074. '(("Junk this notebook" ein:junk-rename)
  1075. ("Open new junk" ein:junk-new))))
  1076. ;; Misc:
  1077. ,@(ein:generate-menu
  1078. '(("Open regular IPython console" ein:console-open)
  1079. ("Open scratch sheet" ein:notebook-scratchsheet-open)
  1080. ("Toggle pseudo console mode" ein:pseudo-console-mode)
  1081. ))
  1082. ))
  1083. map)
  1084. (defun ein:notebook-mode ()
  1085. (funcall (ein:notebook-choose-mode))
  1086. (ein:complete-on-dot-install
  1087. ein:notebook-mode-map 'ein:notebook-complete-dot)
  1088. (ein:aif ein:helm-kernel-history-search-key
  1089. (define-key ein:notebook-mode-map it 'helm-ein-kernel-history))
  1090. (ein:aif ein:anything-kernel-history-search-key
  1091. (define-key ein:notebook-mode-map it 'anything-ein-kernel-history))
  1092. (ein:notebook-minor-mode +1)
  1093. (run-hooks 'ein:notebook-mode-hook))
  1094. (add-hook 'ein:notebook-mode-hook 'ein:worksheet-imenu-setup)
  1095. (define-minor-mode ein:notebook-minor-mode
  1096. "Minor mode to install `ein:notebook-mode-map' for `ein:notebook-mode'."
  1097. :keymap ein:notebook-mode-map
  1098. :group 'ein)
  1099. ;; To avoid MuMaMo to discard `ein:notebook-minor-mode', make it
  1100. ;; permanent local.
  1101. (put 'ein:notebook-minor-mode 'permanent-local t)
  1102. (define-derived-mode ein:notebook-plain-mode fundamental-mode "ein:notebook"
  1103. "IPython notebook mode without fancy coloring."
  1104. (font-lock-mode))
  1105. (define-derived-mode ein:notebook-python-mode python-mode "ein:python"
  1106. "Use `python-mode' for whole notebook buffer.")
  1107. (defun ein:notebook-open-in-browser (&optional print)
  1108. "Open current notebook in web browser.
  1109. When the prefix argument (``C-u``) is given, print page is opened.
  1110. Note that print page is not supported in IPython 0.12.1."
  1111. (interactive "P")
  1112. (let ((url (apply #'ein:url
  1113. (ein:$notebook-url-or-port ein:%notebook%)
  1114. (ein:$notebook-notebook-id ein:%notebook%)
  1115. (if print (list "print")))))
  1116. (message "Opening %s in browser" url)
  1117. (browse-url url)))
  1118. (defun ein:notebook-fetch-data (notebook callback &optional cbargs)
  1119. "Fetch data in body tag of NOTEBOOK html page.
  1120. CALLBACK is called with a plist with data in the body tag as
  1121. the first argument and CBARGS as the rest of arguments."
  1122. (let ((url-or-port (ein:$notebook-url-or-port notebook))
  1123. (notebook-id (ein:$notebook-notebook-id notebook)))
  1124. (ein:query-singleton-ajax
  1125. (list 'notebook-fetch-data url-or-port notebook-id)
  1126. (ein:url url-or-port notebook-id)
  1127. :parser
  1128. (lambda ()
  1129. (list
  1130. :project
  1131. (ein:html-get-data-in-body-tag "data-project")
  1132. :base-project-url
  1133. (ein:html-get-data-in-body-tag "data-base-project-url")
  1134. :base-kernel-url
  1135. (ein:html-get-data-in-body-tag "data-base-kernel-url")
  1136. :read-only
  1137. (ein:html-get-data-in-body-tag "data-read-only")
  1138. :notebook-id
  1139. (ein:html-get-data-in-body-tag "data-notebook-id")))
  1140. :success
  1141. (apply-partially (function*
  1142. (lambda (callback cbargs &key data &allow-other-keys)
  1143. (apply callback data cbargs)))
  1144. callback cbargs))))
  1145. ;;; Buffer and kill hooks
  1146. (defcustom ein:notebook-kill-buffer-ask t
  1147. "Whether EIN should ask before killing unsaved notebook buffer."
  1148. :type '(choice (const :tag "Yes" t)
  1149. (const :tag "No" nil))
  1150. :group 'ein)
  1151. ;; -- `kill-buffer-query-functions'
  1152. (defun ein:notebook-ask-before-kill-buffer ()
  1153. "Return `nil' to prevent killing the notebook buffer.
  1154. Called via `kill-buffer-query-functions'."
  1155. (not (or (and ein:notebook-kill-buffer-ask
  1156. (ein:worksheet-p ein:%worksheet%) ; it's not `ein:scratchsheet'
  1157. (ein:notebook-modified-p)
  1158. (not (y-or-n-p
  1159. "You have unsaved changes. Discard changes?")))
  1160. (when (ein:worksheet-p ein:%worksheet%)
  1161. ;; To make `ein:worksheet-save-cells' no-op.
  1162. (ein:worksheet-dont-save-cells ein:%worksheet%)
  1163. nil))))
  1164. (add-hook 'kill-buffer-query-functions 'ein:notebook-ask-before-kill-buffer)
  1165. ;; -- `kill-emacs-query-functions'
  1166. (defun ein:notebook-ask-before-kill-emacs ()
  1167. "Return `nil' to prevent killing Emacs when unsaved notebook exists.
  1168. Called via `kill-emacs-query-functions'."
  1169. (condition-case err
  1170. (let ((unsaved (ein:filter #'ein:notebook-modified-p
  1171. (ein:notebook-opened-notebooks))))
  1172. (if (null unsaved)
  1173. t
  1174. (let ((answer
  1175. (y-or-n-p
  1176. (format "You have %s unsaved notebook(s). Discard changes?"
  1177. (length unsaved)))))
  1178. ;; kill all unsaved buffers forcefully
  1179. (when answer
  1180. (mapc #'ein:notebook-close unsaved))
  1181. answer)))
  1182. ((debug error)
  1183. (ein:log 'error "Got error: %S" err)
  1184. (y-or-n-p "Error while examine notebooks. Kill Emacs anyway? "))))
  1185. (add-hook 'kill-emacs-query-functions 'ein:notebook-ask-before-kill-emacs)
  1186. ;; -- `kill-buffer-hook'
  1187. (defun ein:notebook-kill-buffer-callback ()
  1188. "Call notebook destructor. This function is called via `kill-buffer-hook'."
  1189. (when (ein:$notebook-p ein:%notebook%)
  1190. (ein:notebook-close-worksheet ein:%notebook% ein:%worksheet%)))
  1191. (defun ein:notebook-setup-kill-buffer-hook ()
  1192. "Add \"notebook destructor\" to `kill-buffer-hook'."
  1193. (add-hook 'kill-buffer-hook 'ein:notebook-kill-buffer-callback))
  1194. ;; Useful command to close notebooks.
  1195. (defun ein:notebook-kill-all-buffers ()
  1196. "Close all opened notebooks."
  1197. (interactive)
  1198. (let* ((notebooks (ein:notebook-opened-notebooks))
  1199. (unsaved (ein:filter #'ein:notebook-modified-p notebooks)))
  1200. (if notebooks
  1201. (if (y-or-n-p
  1202. (format (concat "You have %s opened notebook(s). "
  1203. (when unsaved
  1204. (format "%s are UNSAVED. " (length unsaved)))
  1205. "Really kill all of them?")
  1206. (length notebooks)))
  1207. (progn (ein:log 'info "Killing all notebook buffers...")
  1208. (mapc #'ein:notebook-close notebooks)
  1209. (ein:log 'info "Killing all notebook buffers... Done!"))
  1210. (ein:log 'info "Canceled to kill all notebooks."))
  1211. (ein:log 'info "No opened notebooks."))))
  1212. (provide 'ein-notebook)
  1213. ;;; ein-notebook.el ends here