2012-08-13 17:26:22 +02:00
|
|
|
|
;;; ein-worksheet.el --- Worksheet module
|
|
|
|
|
|
|
|
|
|
;; Copyright (C) 2012 Takafumi Arakaki
|
|
|
|
|
|
|
|
|
|
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
|
|
|
|
|
|
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
|
|
|
|
|
|
;; ein-worksheet.el is free software: you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
;; ein-worksheet.el is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;; along with ein-worksheet.el. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
|
2012-08-13 17:39:39 +02:00
|
|
|
|
(eval-when-compile (require 'cl))
|
|
|
|
|
(require 'eieio)
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(require 'ewoc)
|
|
|
|
|
|
2012-08-28 15:26:32 +02:00
|
|
|
|
(require 'ein-core)
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(require 'ein-cell)
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(require 'ein-kill-ring)
|
2012-08-18 18:28:35 +02:00
|
|
|
|
|
2012-08-28 16:26:08 +02:00
|
|
|
|
|
|
|
|
|
;;; Configuration
|
|
|
|
|
|
|
|
|
|
(define-obsolete-variable-alias
|
|
|
|
|
'ein:notebook-enable-undo 'ein:worksheet-enable-undo "0.2.0")
|
|
|
|
|
|
|
|
|
|
(defcustom ein:worksheet-enable-undo 'yes
|
|
|
|
|
"Configure undo in notebook buffers.
|
|
|
|
|
|
|
|
|
|
`no' : symbol
|
|
|
|
|
Do not use undo in notebook buffers. It is the safest option.
|
|
|
|
|
`yes' : symbol
|
|
|
|
|
Enable undo in notebook buffers. You can't undo after
|
|
|
|
|
modification of cell (execution, add, remove, etc.). This
|
|
|
|
|
is default.
|
|
|
|
|
`full' : symbol
|
|
|
|
|
Enable full undo in notebook buffers. It is powerful but
|
|
|
|
|
sometime (typically after the cell specific commands) undo
|
|
|
|
|
mess up notebook buffer. Use it on your own risk. When the
|
|
|
|
|
buffer is messed up, you can just redo and continue editing,
|
|
|
|
|
or save it once and reopen it if you want to be careful.
|
|
|
|
|
|
|
|
|
|
You need to reopen the notebook buffer to reflect the change of
|
|
|
|
|
this value."
|
|
|
|
|
:type '(choice (const :tag "No" no)
|
|
|
|
|
(const :tag "Yes" yes)
|
|
|
|
|
(const :tag "Full" full))
|
|
|
|
|
:group 'ein)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Configuration getter
|
|
|
|
|
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(defun ein:worksheet-empty-undo-maybe ()
|
2012-08-28 16:26:08 +02:00
|
|
|
|
"Empty `buffer-undo-list' if `ein:worksheet-enable-undo' is `yes'."
|
|
|
|
|
(when (eq ein:worksheet-enable-undo 'yes)
|
|
|
|
|
(setq buffer-undo-list nil)))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
2012-08-18 18:28:35 +02:00
|
|
|
|
|
|
|
|
|
;;; Class and variable
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
|
|
|
|
(defvar ein:worksheet-buffer-name-template "*ein: %s/%s*")
|
2012-08-13 17:39:39 +02:00
|
|
|
|
|
|
|
|
|
(defclass ein:worksheet ()
|
2012-08-28 20:51:18 +02:00
|
|
|
|
((nbformat :initarg :nbformat :type integer)
|
2012-08-28 18:00:07 +02:00
|
|
|
|
(get-notebook-name :initarg :get-notebook-name :type cons)
|
2012-08-28 21:10:23 +02:00
|
|
|
|
;; This slot introduces too much complexity so therefore must be
|
|
|
|
|
;; removed later. This is here only for backward compatible
|
|
|
|
|
;; reason.
|
2012-08-28 20:51:18 +02:00
|
|
|
|
(discard-output-p :initarg :discard-output-p)
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(data :initarg :data)
|
|
|
|
|
(ewoc :initarg :ewoc :type ewoc)
|
|
|
|
|
(kernel :initarg :kernel :type ein:$kernel)
|
2012-08-20 09:56:52 +02:00
|
|
|
|
(dirty :initarg :dirty :type boolean :initform nil)
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(metadata :initarg :metadata :initform nil)
|
2012-08-28 21:43:57 +02:00
|
|
|
|
(events :initarg :events)))
|
2012-08-13 17:39:39 +02:00
|
|
|
|
|
2012-08-18 18:56:37 +02:00
|
|
|
|
(ein:deflocal ein:%worksheet% nil
|
2012-08-16 15:24:45 +02:00
|
|
|
|
"Buffer local variable to store an instance of `ein:worksheet'.")
|
|
|
|
|
|
2012-08-18 18:28:35 +02:00
|
|
|
|
|
|
|
|
|
;;; Initialization of object and buffer
|
|
|
|
|
|
2012-08-28 20:51:18 +02:00
|
|
|
|
(defun ein:worksheet-new (nbformat get-notebook-name discard-output-p
|
2012-08-28 18:00:07 +02:00
|
|
|
|
kernel events &rest args)
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(apply #'make-instance 'ein:worksheet
|
2012-08-28 18:00:07 +02:00
|
|
|
|
:nbformat nbformat :get-notebook-name get-notebook-name
|
2012-08-28 20:51:18 +02:00
|
|
|
|
:discard-output-p discard-output-p :kernel kernel :events events
|
2012-08-19 17:13:09 +02:00
|
|
|
|
args))
|
2012-08-18 18:28:35 +02:00
|
|
|
|
|
|
|
|
|
(defmethod ein:worksheet-bind-events ((ws ein:worksheet))
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(with-slots (events) ws
|
2012-08-20 01:30:51 +02:00
|
|
|
|
;; Bind events for sub components:
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(mapc (lambda (cell) (oset cell :events events))
|
|
|
|
|
(ein:worksheet-get-cells ws))))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
2012-08-27 23:21:47 +02:00
|
|
|
|
(defun ein:worksheet-class-bind-events (events)
|
|
|
|
|
"Binds event handlers which are not needed to be bound per instance."
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:events-on events
|
2012-08-28 16:40:54 +02:00
|
|
|
|
'maybe_reset_undo.Worksheet
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(lambda (-ignore- cell)
|
|
|
|
|
(ein:with-live-buffer (ein:cell-buffer cell)
|
|
|
|
|
(ein:worksheet-empty-undo-maybe))))
|
2012-08-27 23:25:49 +02:00
|
|
|
|
(ein:events-on events 'set_next_input.Worksheet
|
|
|
|
|
#'ein:worksheet--set-next-input)
|
2012-08-27 23:21:47 +02:00
|
|
|
|
(ein:events-on events 'set_dirty.Worksheet #'ein:worksheet--set-dirty))
|
|
|
|
|
|
2012-08-27 23:25:49 +02:00
|
|
|
|
(defun ein:worksheet--set-next-input (-ignore- data)
|
2012-08-20 01:30:51 +02:00
|
|
|
|
(destructuring-bind (&key cell text) data
|
2012-08-27 23:25:49 +02:00
|
|
|
|
(ein:with-live-buffer (ein:cell-buffer cell)
|
|
|
|
|
(ein:and-let* ((ws ein:%worksheet%)
|
|
|
|
|
(new-cell
|
|
|
|
|
(ein:worksheet-insert-cell-below ws 'code cell)))
|
|
|
|
|
(ein:cell-set-text new-cell text)
|
|
|
|
|
(oset ws :dirty t)))))
|
2012-08-20 01:30:51 +02:00
|
|
|
|
|
2012-08-27 23:21:47 +02:00
|
|
|
|
(defun ein:worksheet--set-dirty (-ignore- data)
|
|
|
|
|
"Set dirty flag of worksheet in which CELL in DATA locates."
|
|
|
|
|
(destructuring-bind (&key value cell) data
|
|
|
|
|
(ein:with-live-buffer (ein:cell-buffer cell)
|
2012-08-28 01:38:41 +02:00
|
|
|
|
(ein:worksheet-set-modified-p ein:%worksheet% value))))
|
2012-08-27 23:21:47 +02:00
|
|
|
|
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(defmethod ein:worksheet-notebook-name ((ws ein:worksheet))
|
2012-08-28 18:00:07 +02:00
|
|
|
|
(ein:funcall-packed (oref ws :get-notebook-name)))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
|
|
|
|
(defmethod ein:worksheet-url-or-port ((ws ein:worksheet))
|
2012-08-28 18:03:41 +02:00
|
|
|
|
(ein:kernel-url-or-port (oref ws :kernel)))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
|
|
|
|
(defmethod ein:worksheet-name ((ws ein:worksheet))
|
|
|
|
|
(plist-get (oref ws :metadata) :name))
|
|
|
|
|
|
2012-08-18 18:28:35 +02:00
|
|
|
|
(defmethod ein:worksheet-full-name ((ws ein:worksheet))
|
|
|
|
|
(let ((nb-name (ein:worksheet-notebook-name ws)))
|
|
|
|
|
(ein:aif (ein:worksheet-name ws)
|
|
|
|
|
(concat nb-name "/" it)
|
|
|
|
|
nb-name)))
|
|
|
|
|
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(defmethod ein:worksheet-buffer ((ws ein:worksheet))
|
|
|
|
|
(ein:and-let* (((slot-boundp ws :ewoc))
|
2012-08-19 11:58:04 +02:00
|
|
|
|
(ewoc (oref ws :ewoc))
|
|
|
|
|
(buffer (ewoc-buffer ewoc))
|
|
|
|
|
((buffer-live-p buffer)))
|
|
|
|
|
buffer))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
2012-08-20 03:26:09 +02:00
|
|
|
|
(defmethod ein:worksheet--buffer-name ((ws ein:worksheet))
|
|
|
|
|
(format ein:worksheet-buffer-name-template
|
|
|
|
|
(ein:worksheet-url-or-port ws)
|
|
|
|
|
(ein:worksheet-full-name ws)))
|
|
|
|
|
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(defmethod ein:worksheet--get-buffer ((ws ein:worksheet))
|
|
|
|
|
(or (ein:worksheet-buffer ws)
|
2012-08-20 03:26:09 +02:00
|
|
|
|
(generate-new-buffer (ein:worksheet--buffer-name ws))))
|
|
|
|
|
|
|
|
|
|
(defmethod ein:worksheet-set-buffer-name ((ws ein:worksheet))
|
2012-08-20 13:31:24 +02:00
|
|
|
|
(ein:with-live-buffer (ein:worksheet-buffer ws)
|
2012-08-20 03:26:09 +02:00
|
|
|
|
(rename-buffer (ein:worksheet--buffer-name ws))))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
2012-08-20 11:39:07 +02:00
|
|
|
|
(defmethod ein:worksheet-set-modified-p ((ws ein:worksheet) dirty)
|
|
|
|
|
(ein:with-live-buffer (ein:worksheet-buffer ws)
|
|
|
|
|
(set-buffer-modified-p dirty))
|
|
|
|
|
(oset ws :dirty dirty))
|
|
|
|
|
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(defmethod ein:worksheet-render ((ws ein:worksheet))
|
|
|
|
|
(with-current-buffer (ein:worksheet--get-buffer ws)
|
2012-08-18 18:56:37 +02:00
|
|
|
|
(setq ein:%worksheet% ws)
|
2012-08-16 15:24:45 +02:00
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(let ((ewoc (ein:ewoc-create 'ein:worksheet-pp
|
|
|
|
|
(ein:propertize-read-only "\n")
|
2012-08-24 20:05:39 +02:00
|
|
|
|
nil t))
|
|
|
|
|
(cells (plist-get (oref ws :data) :cells)))
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(oset ws :ewoc ewoc)
|
2012-08-24 20:05:39 +02:00
|
|
|
|
(if cells
|
|
|
|
|
(mapc (lambda (data)
|
|
|
|
|
(ein:cell-enter-last (ein:cell-from-json data :ewoc ewoc)))
|
|
|
|
|
cells)
|
|
|
|
|
(ein:worksheet-insert-cell-below ws 'code nil t))))
|
2012-08-19 11:58:04 +02:00
|
|
|
|
(set-buffer-modified-p nil)
|
2012-08-18 18:28:35 +02:00
|
|
|
|
(setq buffer-undo-list nil) ; clear undo history
|
2012-08-28 16:26:08 +02:00
|
|
|
|
(when (eq ein:worksheet-enable-undo 'no)
|
2012-08-18 18:28:35 +02:00
|
|
|
|
(setq buffer-undo-list t))
|
|
|
|
|
(ein:worksheet-bind-events ws)
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(ein:worksheet-set-kernel ws)
|
2012-08-18 18:28:35 +02:00
|
|
|
|
(ein:log 'info "Worksheet %s is ready" (ein:worksheet-full-name ws))))
|
2012-08-16 15:24:45 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-pp (ewoc-data)
|
|
|
|
|
(let ((path (ein:$node-path ewoc-data))
|
|
|
|
|
(data (ein:$node-data ewoc-data)))
|
|
|
|
|
(case (car path)
|
|
|
|
|
(cell (ein:cell-pp (cdr path) data)))))
|
2012-08-13 17:26:22 +02:00
|
|
|
|
|
2012-08-18 18:28:35 +02:00
|
|
|
|
|
|
|
|
|
;;; Persistance and loading
|
|
|
|
|
|
|
|
|
|
(defmethod ein:worksheet-from-json ((ws ein:worksheet) data)
|
2012-08-19 11:58:04 +02:00
|
|
|
|
(oset ws :data data)
|
|
|
|
|
ws)
|
2012-08-18 18:28:35 +02:00
|
|
|
|
|
2012-08-19 17:40:41 +02:00
|
|
|
|
(defmethod ein:worksheet-to-json ((ws ein:worksheet))
|
2012-08-28 20:51:18 +02:00
|
|
|
|
(let* ((discard-output-p (oref ws :discard-output-p))
|
2012-08-19 17:40:41 +02:00
|
|
|
|
(cells (mapcar (lambda (c)
|
|
|
|
|
(ein:cell-to-json
|
2012-08-28 20:51:18 +02:00
|
|
|
|
c (ein:funcall-packed discard-output-p c)))
|
2012-08-19 17:40:41 +02:00
|
|
|
|
(ein:worksheet-get-cells ws))))
|
|
|
|
|
`((cells . ,(apply #'vector cells)))))
|
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Cell indexing, retrieval, etc.
|
|
|
|
|
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(defmethod ein:worksheet-cell-from-type ((ws ein:worksheet) type &rest args)
|
|
|
|
|
"Create a cell of TYPE (symbol or string)."
|
|
|
|
|
;; FIXME: unify type of TYPE to symbol or string.
|
|
|
|
|
(apply #'ein:cell-from-type
|
|
|
|
|
(format "%s" type)
|
|
|
|
|
:ewoc (oref ws :ewoc)
|
|
|
|
|
:events (oref ws :events)
|
|
|
|
|
args))
|
|
|
|
|
|
2012-08-19 11:58:04 +02:00
|
|
|
|
(defmethod ein:worksheet-get-cells ((ws ein:worksheet))
|
|
|
|
|
(let* ((ewoc (oref ws :ewoc))
|
|
|
|
|
(nodes (ewoc-collect ewoc (lambda (n) (ein:cell-node-p n 'prompt)))))
|
|
|
|
|
(mapcar #'ein:$node-data nodes)))
|
|
|
|
|
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(defmethod ein:worksheet-ncells ((ws ein:worksheet))
|
|
|
|
|
(length (ein:worksheet-get-cells ws)))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-get-current-ewoc-node (&optional pos)
|
|
|
|
|
(ein:aand ein:%worksheet% (oref it :ewoc) (ewoc-locate it pos)))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-get-nearest-cell-ewoc-node (&optional pos max cell-p)
|
|
|
|
|
(ein:and-let* ((ewoc-node (ein:worksheet-get-current-ewoc-node pos)))
|
|
|
|
|
;; FIXME: can be optimized using the argument `max'
|
|
|
|
|
(while (and ewoc-node
|
|
|
|
|
(not (and (ein:cell-ewoc-node-p ewoc-node)
|
|
|
|
|
(if cell-p
|
|
|
|
|
(funcall cell-p
|
|
|
|
|
(ein:cell-from-ewoc-node ewoc-node))
|
|
|
|
|
t))))
|
|
|
|
|
(setq ewoc-node (ewoc-next (oref ein:%worksheet% :ewoc) ewoc-node)))
|
|
|
|
|
ewoc-node))
|
|
|
|
|
|
2012-08-20 03:55:52 +02:00
|
|
|
|
(defun* ein:worksheet-get-current-cell (&key pos noerror
|
|
|
|
|
(cell-p #'ein:basecell-child-p))
|
2012-08-19 19:32:18 +02:00
|
|
|
|
"Return a cell at POS. If POS is not given, it is assumed be the
|
|
|
|
|
current cursor position. When the current buffer is not worksheet
|
|
|
|
|
buffer or there is no cell in the current buffer, return `nil'."
|
|
|
|
|
(let ((cell (ein:cell-from-ewoc-node
|
|
|
|
|
(ein:worksheet-get-current-ewoc-node pos))))
|
2012-08-19 20:21:49 +02:00
|
|
|
|
(if (funcall cell-p cell)
|
2012-08-19 19:32:18 +02:00
|
|
|
|
cell
|
|
|
|
|
(unless noerror
|
|
|
|
|
(error "No cell found at pos=%s" pos)))))
|
|
|
|
|
|
2012-08-27 14:22:27 +02:00
|
|
|
|
(defun ein:worksheet-at-codecell-p ()
|
|
|
|
|
(ein:worksheet-get-current-cell :noerror t :cell-p #'ein:codecell-p))
|
|
|
|
|
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(defun ein:worksheet-get-cells-in-region (beg end)
|
|
|
|
|
(ein:clip-list (ein:aand ein:%worksheet% (ein:worksheet-get-cells it))
|
2012-08-20 03:55:52 +02:00
|
|
|
|
(ein:worksheet-get-current-cell :pos beg)
|
|
|
|
|
(ein:worksheet-get-current-cell :pos end)))
|
2012-08-19 19:32:18 +02:00
|
|
|
|
|
2012-08-20 02:44:46 +02:00
|
|
|
|
(defun* ein:worksheet-get-cells-in-region-or-at-point
|
|
|
|
|
(&key noerror (cell-p #'ein:basecell-child-p))
|
|
|
|
|
(or (ein:filter cell-p
|
|
|
|
|
(if (region-active-p)
|
|
|
|
|
(ein:worksheet-get-cells-in-region (region-beginning)
|
|
|
|
|
(region-end))
|
|
|
|
|
(list (ein:worksheet-get-current-cell))))
|
|
|
|
|
(unless noerror
|
|
|
|
|
(error "Cell not found"))))
|
2012-08-19 19:32:18 +02:00
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Insertion and deletion of cells
|
|
|
|
|
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(defun ein:worksheet--get-ws-or-error ()
|
|
|
|
|
(or ein:%worksheet% (error "Not in worksheet buffer.")))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-focus-cell ()
|
2012-08-20 10:14:21 +02:00
|
|
|
|
(ein:aand (ein:worksheet-get-current-cell :noerror t) (ein:cell-goto it)))
|
2012-08-19 19:32:18 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-delete-cell (ws cell &optional focus)
|
|
|
|
|
"Delete a cell. \(WARNING: no undo!)
|
|
|
|
|
This command has no key binding because there is no way to undo
|
|
|
|
|
deletion. Use kill to play on the safe side.
|
|
|
|
|
|
|
|
|
|
If you really want use this command, you can do something like this
|
|
|
|
|
\(but be careful when using it!)::
|
|
|
|
|
|
|
|
|
|
\(define-key ein:notebook-mode-map \"\\C-c\\C-d\"
|
|
|
|
|
'ein:worksheet-delete-cell)"
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)
|
|
|
|
|
t))
|
|
|
|
|
(let ((inhibit-read-only t)
|
|
|
|
|
(buffer-undo-list t)) ; disable undo recording
|
|
|
|
|
(apply #'ewoc-delete
|
|
|
|
|
(oref ws :ewoc)
|
|
|
|
|
(ein:cell-all-element cell)))
|
|
|
|
|
(oset ws :dirty t)
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(when focus (ein:worksheet-focus-cell)))
|
|
|
|
|
|
2012-08-20 11:21:18 +02:00
|
|
|
|
(defun ein:worksheet-kill-cell (ws cells &optional focus)
|
2012-08-19 19:32:18 +02:00
|
|
|
|
"Kill (\"cut\") the cell at point or cells in region.
|
|
|
|
|
Note that the kill-ring for cells is not shared with the default
|
|
|
|
|
kill-ring of Emacs (kill-ring for texts)."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-cells-in-region-or-at-point)
|
|
|
|
|
t))
|
|
|
|
|
(when cells
|
|
|
|
|
(mapc (lambda (c)
|
|
|
|
|
(ein:cell-save-text c)
|
|
|
|
|
(ein:worksheet-delete-cell ws c)
|
|
|
|
|
(ein:cell-deactivate c))
|
|
|
|
|
cells)
|
|
|
|
|
(ein:kill-new cells)
|
|
|
|
|
(when focus
|
|
|
|
|
(deactivate-mark)
|
|
|
|
|
(ein:worksheet-focus-cell))))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-copy-cell (cells)
|
|
|
|
|
"Copy the cell at point. (Put the current cell into the kill-ring.)"
|
|
|
|
|
(interactive
|
|
|
|
|
(list (when (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(prog1 (ein:worksheet-get-cells-in-region-or-at-point)
|
|
|
|
|
(deactivate-mark)))))
|
|
|
|
|
(let ((cells (mapcar
|
|
|
|
|
(lambda (c)
|
|
|
|
|
(ein:cell-deactivate (ein:cell-copy c))) cells)))
|
|
|
|
|
(ein:log 'info "%s cells are copied." (length cells))
|
|
|
|
|
(ein:kill-new cells)))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-insert-clone-below (ws cell pivot)
|
|
|
|
|
(let ((clone (ein:cell-copy cell)))
|
|
|
|
|
;; Cell can be from another buffer, so reset `ewoc'.
|
|
|
|
|
(oset clone :ewoc (oref ws :ewoc))
|
|
|
|
|
(ein:worksheet-insert-cell-below ws clone pivot)
|
|
|
|
|
clone))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-yank-cell (ws &optional n)
|
|
|
|
|
"Insert (\"paste\") the latest killed cell.
|
|
|
|
|
Prefixes are act same as the normal `yank' command."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(let ((arg current-prefix-arg))
|
|
|
|
|
(cond ((listp arg) 0)
|
|
|
|
|
((eq arg '-) -2)
|
|
|
|
|
(t (1- arg))))))
|
2012-08-19 20:21:49 +02:00
|
|
|
|
(let* ((cell (ein:worksheet-get-current-cell :noerror t)) ; can be nil
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(killed (ein:current-kill n)))
|
|
|
|
|
(loop for c in killed
|
|
|
|
|
with last = cell
|
|
|
|
|
do (setq last (ein:worksheet-insert-clone-below ws c last))
|
|
|
|
|
finally (ein:cell-goto last))))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-maybe-new-cell (ws type-or-cell)
|
|
|
|
|
"Return TYPE-OR-CELL as-is if it is a cell, otherwise return a new cell."
|
|
|
|
|
(let ((cell (if (ein:basecell-child-p type-or-cell)
|
|
|
|
|
type-or-cell
|
|
|
|
|
(ein:worksheet-cell-from-type ws type-or-cell))))
|
|
|
|
|
;; When newly created or copied, kernel is not attached or not the
|
|
|
|
|
;; kernel of this worksheet. So reset it here.
|
|
|
|
|
(when (ein:codecell-p cell)
|
|
|
|
|
(oset cell :kernel (oref ws :kernel)))
|
|
|
|
|
(oset cell :events (oref ws :events))
|
|
|
|
|
cell))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-insert-cell-below (ws type-or-cell pivot &optional focus)
|
|
|
|
|
"Insert cell below. Insert markdown cell instead of code cell
|
|
|
|
|
when the prefix argument is given.
|
|
|
|
|
|
|
|
|
|
When used as a lisp function, insert a cell of TYPE-OR-CELL just
|
|
|
|
|
after PIVOT and return the new cell."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
2012-08-20 12:47:09 +02:00
|
|
|
|
(if current-prefix-arg 'markdown 'code)
|
2012-08-19 20:21:49 +02:00
|
|
|
|
(ein:worksheet-get-current-cell :noerror t) ; can be nil
|
2012-08-19 19:32:18 +02:00
|
|
|
|
t))
|
|
|
|
|
(let ((cell (ein:worksheet-maybe-new-cell ws type-or-cell)))
|
|
|
|
|
(cond
|
|
|
|
|
((= (ein:worksheet-ncells ws) 0)
|
|
|
|
|
(ein:cell-enter-last cell))
|
|
|
|
|
(pivot
|
|
|
|
|
(ein:cell-insert-below pivot cell))
|
|
|
|
|
(t (error
|
|
|
|
|
"PIVOT is `nil' but ncells != 0. There is something wrong...")))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(oset ws :dirty t)
|
|
|
|
|
(when focus (ein:cell-goto cell))
|
|
|
|
|
cell))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-insert-cell-above (ws type-or-cell pivot &optional focus)
|
|
|
|
|
"Insert cell above. Insert markdown cell instead of code cell
|
|
|
|
|
when the prefix argument is given.
|
|
|
|
|
See also: `ein:worksheet-insert-cell-below'."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
2012-08-20 12:47:09 +02:00
|
|
|
|
(if current-prefix-arg 'markdown 'code)
|
2012-08-19 20:21:49 +02:00
|
|
|
|
(ein:worksheet-get-current-cell :noerror t) ; can be nil
|
2012-08-19 19:32:18 +02:00
|
|
|
|
t))
|
|
|
|
|
(let ((cell (ein:worksheet-maybe-new-cell ws type-or-cell)))
|
|
|
|
|
(cond
|
|
|
|
|
((< (ein:worksheet-ncells ws) 2)
|
|
|
|
|
(ein:cell-enter-first cell))
|
|
|
|
|
(pivot
|
|
|
|
|
(let ((prev-cell (ein:cell-prev pivot)))
|
|
|
|
|
(if prev-cell
|
|
|
|
|
(ein:cell-insert-below prev-cell cell)
|
|
|
|
|
(ein:cell-enter-first cell))))
|
|
|
|
|
(t (error
|
|
|
|
|
"PIVOT is `nil' but ncells > 0. There is something wrong...")))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:32:18 +02:00
|
|
|
|
(oset ws :dirty t)
|
|
|
|
|
(when focus (ein:cell-goto cell))
|
|
|
|
|
cell))
|
|
|
|
|
|
2012-08-19 19:40:18 +02:00
|
|
|
|
(defun ein:worksheet-toggle-cell-type (ws cell &optional focus)
|
|
|
|
|
"Toggle the cell type of the cell at point.
|
|
|
|
|
Use `ein:worksheet-change-cell-type' to change the cell type
|
|
|
|
|
directly."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)
|
|
|
|
|
t))
|
2012-08-28 17:38:16 +02:00
|
|
|
|
(let ((type (case (oref ws :nbformat)
|
2012-08-19 19:40:18 +02:00
|
|
|
|
(2 (ein:case-equal (oref cell :cell-type)
|
|
|
|
|
(("code") "markdown")
|
|
|
|
|
(("markdown") "code")))
|
|
|
|
|
(3 (ein:case-equal (oref cell :cell-type)
|
|
|
|
|
(("code") "markdown")
|
|
|
|
|
(("markdown") "raw")
|
|
|
|
|
(("raw") "heading")
|
|
|
|
|
(("heading") "code"))))))
|
|
|
|
|
(let ((relpos (ein:cell-relative-point cell))
|
|
|
|
|
(new (ein:cell-convert-inplace cell type)))
|
|
|
|
|
(when (ein:codecell-p new)
|
|
|
|
|
(oset new :kernel (oref ws :kernel)))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:40:18 +02:00
|
|
|
|
(when focus (ein:cell-goto new relpos)))))
|
2012-08-19 19:45:21 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-change-cell-type (ws cell type &optional level focus)
|
|
|
|
|
"Change the cell type of the current cell.
|
|
|
|
|
Prompt will appear in the minibuffer.
|
|
|
|
|
|
|
|
|
|
When used in as a Lisp function, TYPE (string) should be chose
|
|
|
|
|
from \"code\", \"markdown\", \"raw\" and \"heading\". LEVEL is
|
|
|
|
|
an integer used only when the TYPE is \"heading\"."
|
|
|
|
|
(interactive
|
|
|
|
|
(let* ((ws (ein:worksheet--get-ws-or-error))
|
|
|
|
|
(cell (ein:worksheet-get-current-cell))
|
2012-08-28 17:38:16 +02:00
|
|
|
|
(choices (case (oref ws :nbformat)
|
2012-08-19 19:45:21 +02:00
|
|
|
|
(2 "cm")
|
|
|
|
|
(3 "cmr123456")))
|
|
|
|
|
(key (ein:ask-choice-char
|
|
|
|
|
(format "Cell type [%s]: " choices) choices))
|
|
|
|
|
(type (case key
|
|
|
|
|
(?c "code")
|
|
|
|
|
(?m "markdown")
|
|
|
|
|
(?r "raw")
|
|
|
|
|
(t "heading")))
|
|
|
|
|
(level (when (equal type "heading")
|
|
|
|
|
(string-to-number (char-to-string key)))))
|
|
|
|
|
(list ws cell type level t)))
|
|
|
|
|
|
|
|
|
|
(let ((relpos (ein:cell-relative-point cell))
|
|
|
|
|
(new (ein:cell-convert-inplace cell type)))
|
|
|
|
|
(when (ein:codecell-p new)
|
|
|
|
|
(oset new :kernel (oref ws :kernel)))
|
|
|
|
|
(when level
|
|
|
|
|
(ein:cell-change-level new level))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:45:21 +02:00
|
|
|
|
(when focus (ein:cell-goto new relpos))))
|
|
|
|
|
|
2012-08-19 19:53:00 +02:00
|
|
|
|
(defun ein:worksheet-split-cell-at-point (ws cell &optional no-trim focus)
|
|
|
|
|
"Split cell at current position. Newlines at the splitting
|
|
|
|
|
point will be removed. This can be omitted by giving a prefix
|
|
|
|
|
argument \(C-u)."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)
|
2012-08-20 12:47:09 +02:00
|
|
|
|
current-prefix-arg
|
2012-08-19 19:53:00 +02:00
|
|
|
|
t))
|
|
|
|
|
;; FIXME: should I inhibit undo?
|
|
|
|
|
(let* ((beg (set-marker (make-marker) (ein:cell-input-pos-min cell)))
|
|
|
|
|
(pos (point-marker))
|
|
|
|
|
(head (buffer-substring beg pos))
|
|
|
|
|
(new (ein:worksheet-insert-cell-above ws
|
|
|
|
|
(oref cell :cell-type)
|
|
|
|
|
cell)))
|
2012-08-21 11:13:16 +02:00
|
|
|
|
(when (ein:headingcell-p cell)
|
|
|
|
|
(ein:cell-change-level new (oref cell :level)))
|
2012-08-19 19:53:00 +02:00
|
|
|
|
(delete-region beg pos)
|
|
|
|
|
(unless no-trim
|
|
|
|
|
(setq head (ein:trim-right head "\n"))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char pos)
|
2012-08-20 12:35:30 +02:00
|
|
|
|
(let ((end (set-marker (make-marker) (ein:cell-input-pos-max cell))))
|
|
|
|
|
(while (and (looking-at-p "\n") (< (point) end))
|
|
|
|
|
(delete-char 1)))))
|
2012-08-19 19:53:00 +02:00
|
|
|
|
(ein:cell-set-text new head)
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:53:00 +02:00
|
|
|
|
(when focus (ein:cell-goto cell))))
|
|
|
|
|
|
2012-08-19 19:56:56 +02:00
|
|
|
|
(defun ein:worksheet-merge-cell (ws cell &optional next focus)
|
|
|
|
|
"Merge previous cell into current cell.
|
|
|
|
|
If prefix is given, merge current cell into next cell."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)
|
2012-08-20 12:47:09 +02:00
|
|
|
|
current-prefix-arg
|
2012-08-19 19:56:56 +02:00
|
|
|
|
t))
|
|
|
|
|
(unless next
|
|
|
|
|
(setq cell (ein:cell-prev cell))
|
|
|
|
|
(unless cell (error "No previous cell"))
|
|
|
|
|
(ein:cell-goto cell))
|
|
|
|
|
(let* ((next-cell (ein:cell-next cell))
|
|
|
|
|
(head (ein:cell-get-text cell)))
|
|
|
|
|
(assert next-cell nil "No cell to merge.")
|
|
|
|
|
(ein:worksheet-delete-cell ws cell)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (ein:cell-input-pos-min next-cell))
|
|
|
|
|
(insert head "\n"))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 19:56:56 +02:00
|
|
|
|
(when focus (ein:cell-goto next-cell))))
|
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Cell selection.
|
|
|
|
|
|
2012-08-19 20:03:53 +02:00
|
|
|
|
(defun ein:worksheet-goto-input (ewoc-node up)
|
|
|
|
|
(let* ((ewoc-data (ewoc-data ewoc-node))
|
|
|
|
|
(cell (ein:$node-data ewoc-data))
|
|
|
|
|
(path (ein:$node-path ewoc-data))
|
|
|
|
|
(element (nth 1 path)))
|
|
|
|
|
(ein:aif
|
|
|
|
|
(if (memql element (if up '(output footer) '(prompt)))
|
|
|
|
|
cell
|
|
|
|
|
(funcall (if up #'ein:cell-prev #'ein:cell-next) cell))
|
|
|
|
|
(ein:cell-goto it)
|
|
|
|
|
(error "No %s input!" (if up "previous" "next")))))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-goto-next-input (ewoc-node)
|
|
|
|
|
(interactive (list (and (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-ewoc-node))))
|
|
|
|
|
(ein:worksheet-goto-input ewoc-node nil))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-goto-prev-input (ewoc-node)
|
|
|
|
|
(interactive (list (and (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-ewoc-node))))
|
|
|
|
|
(ein:worksheet-goto-input ewoc-node t))
|
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Cell movement
|
|
|
|
|
|
2012-08-19 20:09:25 +02:00
|
|
|
|
(defun ein:worksheet-move-cell (ws cell up)
|
|
|
|
|
(ein:aif (if up (ein:cell-prev cell) (ein:cell-next cell))
|
|
|
|
|
(let ((inhibit-read-only t)
|
|
|
|
|
(pivot-cell it))
|
|
|
|
|
(ein:cell-save-text cell)
|
|
|
|
|
(ein:worksheet-delete-cell ws cell)
|
|
|
|
|
(funcall (if up
|
|
|
|
|
#'ein:worksheet-insert-cell-above
|
|
|
|
|
#'ein:worksheet-insert-cell-below)
|
|
|
|
|
ws cell pivot-cell)
|
|
|
|
|
(ein:cell-goto cell)
|
|
|
|
|
(oset ws :dirty t))
|
|
|
|
|
(error "No %s cell" (if up "previous" "next"))))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-move-cell-up (ws cell)
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)))
|
|
|
|
|
(ein:worksheet-move-cell ws cell t))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-move-cell-down (ws cell)
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)))
|
|
|
|
|
(ein:worksheet-move-cell ws cell nil))
|
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Cell collapsing and output clearing
|
|
|
|
|
|
2012-08-19 20:21:49 +02:00
|
|
|
|
(defun ein:worksheet-toggle-output (ws cell)
|
|
|
|
|
"Toggle the visibility of the output of the cell at point.
|
|
|
|
|
This does not alter the actual data stored in the cell."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell
|
|
|
|
|
:cell-p #'ein:codecell-p)))
|
|
|
|
|
(ein:cell-toggle-output cell)
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 20:21:49 +02:00
|
|
|
|
(oset ws :dirty t))
|
|
|
|
|
|
2012-08-26 18:16:53 +02:00
|
|
|
|
(defun ein:worksheet-set-output-visibility-all (ws &optional collapsed)
|
|
|
|
|
"Show all cell output. When prefix is given, hide all cell output."
|
2012-08-20 12:47:09 +02:00
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error) current-prefix-arg))
|
2012-08-26 18:02:03 +02:00
|
|
|
|
(when collapsed (setq collapsed t)) ; force it to be a boolean
|
2012-08-19 20:24:17 +02:00
|
|
|
|
(mapc (lambda (c)
|
|
|
|
|
(when (ein:codecell-p c) (ein:cell-set-collapsed c collapsed)))
|
|
|
|
|
(ein:worksheet-get-cells ws))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe)
|
2012-08-19 20:24:17 +02:00
|
|
|
|
(oset ws :dirty t))
|
|
|
|
|
|
2012-08-19 20:26:46 +02:00
|
|
|
|
(defun ein:worksheet-clear-output (cell &optional preserve-input-prompt)
|
|
|
|
|
"Clear output from the current cell at point.
|
|
|
|
|
Do not clear input prompt when the prefix argument is given."
|
|
|
|
|
(interactive (list (ein:worksheet-get-current-cell
|
|
|
|
|
:cell-p #'ein:codecell-p)
|
2012-08-20 12:47:09 +02:00
|
|
|
|
current-prefix-arg))
|
2012-08-19 20:26:46 +02:00
|
|
|
|
(ein:cell-clear-output cell t t t)
|
|
|
|
|
(unless preserve-input-prompt
|
|
|
|
|
(ein:cell-set-input-prompt cell))
|
2012-08-28 16:39:32 +02:00
|
|
|
|
(ein:worksheet-empty-undo-maybe))
|
2012-08-19 20:26:46 +02:00
|
|
|
|
|
2012-08-19 20:31:46 +02:00
|
|
|
|
(defun ein:worksheet-clear-all-output (ws &optional preserve-input-prompt)
|
|
|
|
|
"Clear output from all cells.
|
|
|
|
|
Do not clear input prompts when the prefix argument is given."
|
2012-08-20 12:47:09 +02:00
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error) current-prefix-arg))
|
2012-08-19 20:31:46 +02:00
|
|
|
|
(mapc (lambda (c) (ein:worksheet-clear-output c preserve-input-prompt))
|
|
|
|
|
(ein:filter #'ein:codecell-p (ein:worksheet-get-cells ws))))
|
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Kernel related things
|
|
|
|
|
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(defmethod ein:worksheet-set-kernel ((ws ein:worksheet))
|
2012-08-19 11:58:04 +02:00
|
|
|
|
(mapc (lambda (cell) (oset cell :kernel (oref ws :kernel)))
|
2012-08-19 17:13:09 +02:00
|
|
|
|
(ein:filter #'ein:codecell-p (ein:worksheet-get-cells ws))))
|
2012-08-19 11:58:04 +02:00
|
|
|
|
|
2012-08-19 20:37:29 +02:00
|
|
|
|
(defun ein:worksheet-execute-cell (ws cell)
|
|
|
|
|
"Execute code type CELL."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell
|
|
|
|
|
:cell-p #'ein:codecell-p)))
|
|
|
|
|
(ein:kernel-if-ready (oref ws :kernel)
|
|
|
|
|
(ein:cell-execute cell)
|
|
|
|
|
(oset ws :dirty t)
|
|
|
|
|
cell))
|
|
|
|
|
|
2012-08-21 11:52:28 +02:00
|
|
|
|
(defun ein:worksheet-execute-cell-and-goto-next (ws cell &optional insert)
|
2012-08-19 20:40:27 +02:00
|
|
|
|
"Execute cell at point if it is a code cell and move to the
|
|
|
|
|
next cell, or insert if none."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)))
|
|
|
|
|
(when (ein:codecell-p cell)
|
|
|
|
|
(ein:worksheet-execute-cell ws cell))
|
2012-08-21 11:52:28 +02:00
|
|
|
|
(ein:aif (and (not insert) (ein:cell-next cell))
|
2012-08-19 20:40:27 +02:00
|
|
|
|
(ein:cell-goto it)
|
2012-08-20 12:58:09 +02:00
|
|
|
|
(ein:worksheet-insert-cell-below ws 'code cell t)))
|
2012-08-19 20:40:27 +02:00
|
|
|
|
|
2012-08-21 11:52:43 +02:00
|
|
|
|
(defun ein:worksheet-execute-cell-and-insert-below (ws cell)
|
|
|
|
|
"Execute cell at point if it is a code cell and insert a
|
|
|
|
|
cell bellow."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)))
|
|
|
|
|
(ein:worksheet-execute-cell-and-goto-next ws cell t))
|
|
|
|
|
|
2012-08-19 20:42:29 +02:00
|
|
|
|
(defun ein:worksheet-execute-all-cell (ws)
|
|
|
|
|
"Execute all cells in the current worksheet buffer."
|
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)))
|
|
|
|
|
(mapc #'ein:cell-execute
|
|
|
|
|
(ein:filter #'ein:codecell-p (ein:worksheet-get-cells ws))))
|
|
|
|
|
|
2012-08-28 12:23:47 +02:00
|
|
|
|
(defun ein:worksheet-insert-last-input-history (ws cell index)
|
2012-08-28 12:34:24 +02:00
|
|
|
|
"Insert INDEX-th previous history into CELL in worksheet WS."
|
2012-08-28 12:23:47 +02:00
|
|
|
|
(ein:kernel-history-request
|
|
|
|
|
(oref ws :kernel)
|
|
|
|
|
(list
|
|
|
|
|
:history_reply
|
|
|
|
|
(cons
|
|
|
|
|
(lambda (cell content -metadata-not-used-)
|
|
|
|
|
(destructuring-bind (session line-number input)
|
|
|
|
|
(car (plist-get content :history))
|
|
|
|
|
(if (eq (ein:worksheet-get-current-cell) cell)
|
|
|
|
|
(ein:cell-set-text cell input)
|
|
|
|
|
(ein:log 'warning
|
|
|
|
|
"Cursor moved from the cell after history request."))
|
|
|
|
|
(ein:log 'info "Input history inserted: session:%d line:%d"
|
|
|
|
|
session line-number)))
|
|
|
|
|
cell))
|
|
|
|
|
:hist-access-type "range"
|
|
|
|
|
:session 0
|
|
|
|
|
:start (- index)
|
|
|
|
|
:stop (- 1 index)))
|
|
|
|
|
|
2012-08-28 13:10:37 +02:00
|
|
|
|
(defvar ein:worksheet--history-index 1)
|
2012-08-28 12:23:47 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:worksheet--get-history-index (inc)
|
2012-08-28 12:34:24 +02:00
|
|
|
|
"Increment history index by (possibly negative) INC.
|
|
|
|
|
Get history index for `ein:worksheet-previous-input-history' and
|
|
|
|
|
`ein:worksheet-next-input-history'. Raise error if caller tries
|
2012-08-28 13:10:37 +02:00
|
|
|
|
to decrement index to less than or equal to 1."
|
2012-08-28 12:23:47 +02:00
|
|
|
|
(if (or (eq last-command 'ein:worksheet-previous-input-history)
|
|
|
|
|
(eq last-command 'ein:worksheet-next-input-history))
|
|
|
|
|
(progn
|
|
|
|
|
(setq ein:worksheet--history-index
|
|
|
|
|
(+ ein:worksheet--history-index inc))
|
2012-08-28 13:10:37 +02:00
|
|
|
|
(when (< ein:worksheet--history-index 1)
|
|
|
|
|
(setq ein:worksheet--history-index 1)
|
2012-08-28 12:23:47 +02:00
|
|
|
|
(error "This is the latest input"))
|
|
|
|
|
ein:worksheet--history-index)
|
2012-08-28 13:10:37 +02:00
|
|
|
|
(setq ein:worksheet--history-index 1)))
|
2012-08-28 12:23:47 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-previous-input-history (ws cell index)
|
2012-08-28 12:34:24 +02:00
|
|
|
|
"Insert the previous input in the execution history.
|
|
|
|
|
You can go back further in the history by repeating this command.
|
2012-08-28 12:40:57 +02:00
|
|
|
|
Use `ein:worksheet-next-input-history' to go forward in the
|
2012-08-28 12:34:24 +02:00
|
|
|
|
history."
|
2012-08-28 12:23:47 +02:00
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)
|
|
|
|
|
(ein:worksheet--get-history-index +1)))
|
|
|
|
|
(ein:worksheet-insert-last-input-history ws cell index))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-next-input-history (ws cell index)
|
2012-08-28 12:34:24 +02:00
|
|
|
|
"Insert next input in the execution history.
|
|
|
|
|
You can go forward further in the history by repeating this
|
2012-08-28 12:40:57 +02:00
|
|
|
|
command. Use `ein:worksheet-previous-input-history' to go back
|
|
|
|
|
in the history."
|
2012-08-28 12:23:47 +02:00
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)
|
|
|
|
|
(ein:worksheet-get-current-cell)
|
|
|
|
|
(ein:worksheet--get-history-index -1)))
|
|
|
|
|
(ein:worksheet-insert-last-input-history ws cell index))
|
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Generic getter
|
|
|
|
|
|
2012-08-20 00:58:45 +02:00
|
|
|
|
(defun ein:get-url-or-port--worksheet ()
|
|
|
|
|
(when (ein:worksheet-p ein:%worksheet%)
|
|
|
|
|
(ein:worksheet-url-or-port ein:%worksheet%)))
|
|
|
|
|
|
|
|
|
|
(defun ein:get-kernel--worksheet ()
|
|
|
|
|
(when (ein:worksheet-p ein:%worksheet%) (oref ein:%worksheet% :kernel)))
|
|
|
|
|
|
2012-08-20 09:42:31 +02:00
|
|
|
|
(defun ein:get-cell-at-point--worksheet ()
|
|
|
|
|
(ein:worksheet-get-current-cell :noerror t))
|
2012-08-20 00:58:45 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:get-traceback-data--worksheet ()
|
2012-08-20 09:42:31 +02:00
|
|
|
|
(ein:aand (ein:get-cell-at-point--worksheet) (ein:cell-get-tb-data it)))
|
2012-08-20 00:58:45 +02:00
|
|
|
|
|
2012-08-20 02:05:56 +02:00
|
|
|
|
|
|
|
|
|
;;; Predicate
|
|
|
|
|
|
2012-08-26 21:38:18 +02:00
|
|
|
|
(defun ein:worksheet-buffer-p ()
|
|
|
|
|
"Return non-`nil' if the current buffer is a worksheet buffer."
|
|
|
|
|
ein:%worksheet%)
|
|
|
|
|
|
2012-08-20 02:05:56 +02:00
|
|
|
|
(defmethod ein:worksheet-modified-p ((ws ein:worksheet))
|
|
|
|
|
(let ((buffer (ein:worksheet-buffer ws)))
|
|
|
|
|
(and (buffer-live-p buffer)
|
|
|
|
|
(or (oref ws :dirty)
|
|
|
|
|
(buffer-modified-p buffer)))))
|
|
|
|
|
|
2012-08-20 02:44:46 +02:00
|
|
|
|
|
|
|
|
|
;;; Auto-execution
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-toggle-autoexec (cell)
|
|
|
|
|
"Toggle auto-execution flag of the cell at point."
|
|
|
|
|
(interactive (list (ein:worksheet-get-current-cell #'ein:codecell-p)))
|
|
|
|
|
(ein:cell-toggle-autoexec cell))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-turn-on-autoexec (cells &optional off)
|
|
|
|
|
"Turn on auto-execution flag of the cells in region or cell at point.
|
|
|
|
|
When the prefix argument is given, turn off the flag instead.
|
|
|
|
|
|
|
|
|
|
To use autoexec feature, you need to turn on auto-execution mode
|
|
|
|
|
in connected buffers, using the `ein:connect-toggle-autoexec'
|
|
|
|
|
command."
|
|
|
|
|
(interactive
|
2012-08-21 08:24:07 +02:00
|
|
|
|
(list (ein:worksheet-get-cells-in-region-or-at-point
|
|
|
|
|
:cell-p #'ein:codecell-p)
|
2012-08-20 02:44:46 +02:00
|
|
|
|
current-prefix-arg))
|
|
|
|
|
(mapc (lambda (c) (ein:cell-set-autoexec c (not off))) cells)
|
|
|
|
|
(ein:log 'info "Turn %s auto-execution flag of %s cells."
|
|
|
|
|
(if off "off" "on")
|
|
|
|
|
(length cells)))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-execute-autoexec-cells (ws)
|
2012-08-20 11:55:00 +02:00
|
|
|
|
"Execute cells of which auto-execution flag is on.
|
2012-08-21 08:29:58 +02:00
|
|
|
|
This function internally sets current buffer to the worksheet
|
|
|
|
|
buffer, so you don't need to set current buffer to call this
|
2012-08-20 11:55:00 +02:00
|
|
|
|
function."
|
2012-08-20 02:44:46 +02:00
|
|
|
|
(interactive (list (ein:worksheet--get-ws-or-error)))
|
2012-08-21 08:25:37 +02:00
|
|
|
|
(ein:with-live-buffer (ein:worksheet-buffer ws)
|
2012-08-20 11:55:00 +02:00
|
|
|
|
(ein:kernel-if-ready (oref ws :kernel)
|
|
|
|
|
(mapc #'ein:cell-execute
|
|
|
|
|
(ein:filter #'ein:cell-autoexec-p
|
|
|
|
|
(ein:worksheet-get-cells ws))))))
|
2012-08-20 02:44:46 +02:00
|
|
|
|
|
2012-08-18 18:28:53 +02:00
|
|
|
|
|
|
|
|
|
;;; Imenu
|
|
|
|
|
|
2012-08-20 01:10:51 +02:00
|
|
|
|
(defun ein:worksheet-imenu-create-index ()
|
|
|
|
|
"`imenu-create-index-function' for notebook buffer."
|
|
|
|
|
;; As Imenu does not provide the way to represent level *and*
|
|
|
|
|
;; position, use #'s to do that.
|
|
|
|
|
(loop for cell in (when (ein:worksheet-p ein:%worksheet%)
|
|
|
|
|
(ein:filter #'ein:headingcell-p
|
|
|
|
|
(ein:worksheet-get-cells ein:%worksheet%)))
|
|
|
|
|
for sharps = (loop repeat (oref cell :level) collect "#")
|
|
|
|
|
for text = (ein:cell-get-text cell)
|
|
|
|
|
for name = (ein:join-str "" (append sharps (list " " text)))
|
|
|
|
|
collect (cons name (ein:cell-input-pos-min cell))))
|
|
|
|
|
|
|
|
|
|
(defun ein:worksheet-imenu-setup ()
|
|
|
|
|
"Called via notebook mode hooks."
|
|
|
|
|
(setq imenu-create-index-function #'ein:worksheet-imenu-create-index))
|
|
|
|
|
|
2012-08-13 17:26:22 +02:00
|
|
|
|
(provide 'ein-worksheet)
|
|
|
|
|
|
|
|
|
|
;;; ein-worksheet.el ends here
|