-;;; ses.el -- Simple Emacs Spreadsheet
+;;; ses.el -- Simple Emacs Spreadsheet -*- coding: utf-8 -*-
-;; Copyright (C) 2002,03,04 Free Software Foundation, Inc.
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
;; Author: Jonathan Yavner <jyavner@member.fsf.org>
;; Maintainer: Jonathan Yavner <jyavner@member.fsf.org>
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
;;; To-do list:
+
;; * Use $ or … for truncated fields
;; * Add command to make a range of columns be temporarily invisible.
;; * Allow paste of one cell to a range of cells -- copy formula to each.
;; * Left-margin column for row number.
;; * Move a row by dragging its number in the left-margin.
+
+;;; Code:
+
(require 'unsafep)
-;;;----------------------------------------------------------------------------
-;;;; User-customizable variables
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; User-customizable variables
+;;----------------------------------------------------------------------------
(defgroup ses nil
- "Simple Emacs Spreadsheet"
+ "Simple Emacs Spreadsheet."
:group 'applications
:prefix "ses-"
:version "21.1")
function))
(defcustom ses-after-entry-functions '(forward-char)
- "Things to do after entering a value into a cell. An abnormal hook that
-usually runs a cursor-movement function. Each function is called with ARG=1."
+ "Things to do after entering a value into a cell.
+An abnormal hook that usually runs a cursor-movement function.
+Each function is called with ARG=1."
:group 'ses
:type 'hook
:options '(forward-char backward-char next-line previous-line))
:type 'hook)
-;;;----------------------------------------------------------------------------
-;;;; Global variables and constants
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Global variables and constants
+;;----------------------------------------------------------------------------
(defvar ses-read-cell-history nil
"List of formulas that have been typed in.")
"Context menu when mouse-3 is used on the header-line in an SES buffer."
'("SES header row"
["Set current row" ses-set-header-row t]
- ["Unset row" ses-unset-header-row (> header-row 0)]))
+ ["Unset row" ses-unset-header-row (> ses--header-row 0)]))
(defconst ses-mode-map
(let ((keys `("\C-c\M-\C-l" ses-reconstruct-all
(newmap (make-sparse-keymap)))
(set-keymap-parent newmap minibuffer-local-map)
(while keys
- (define-key newmap (car keys) (cadr keys))
- (setq keys (cddr keys)))
+ (define-key newmap (pop keys) (pop keys)))
newmap)
"Local keymap for SES minibuffer cell-editing.")
map))
(defconst ses-print-data-boundary "\n\014\n"
- "Marker string denoting the boundary between print area and data area")
+ "Marker string denoting the boundary between print area and data area.")
(defconst ses-initial-global-parameters
"\n( ;Global parameters (these are read first)\n 2 ;SES file-format\n 1 ;numrows\n 1 ;numcols\n)\n\n"
- "Initial contents for the three-element list at the bottom of the data area")
+ "Initial contents for the three-element list at the bottom of the data area.")
(defconst ses-initial-file-trailer
- ";;; Local Variables:\n;;; mode: ses\n;;; End:\n"
+ ";; Local Variables:\n;; mode: ses\n;; End:\n"
"Initial contents for the file-trailer area at the bottom of the file.")
(defconst ses-initial-file-contents
ses-initial-file-trailer)
"The initial contents of an empty spreadsheet.")
-(defconst ses-cell-size 4
- "A cell consists of a SYMBOL, a FORMULA, a PRINTER-function, and a list of
-REFERENCES.")
-
-(defconst ses-paramlines-plist
- '(ses--col-widths 2 ses--col-printers 3 ses--default-printer 4
- ses--header-row 5 ses--file-format 8 ses--numrows 9
- ses--numcols 10)
- "Offsets from last cell line to various parameter lines in the data area
-of a spreadsheet.")
-
(defconst ses-box-prop '(:box (:line-width 2 :style released-button))
"Display properties to create a raised box for cells in the header line.")
column printer or a global-default printer because they invoke the column or
default printer and then modify its output.")
+
+;;----------------------------------------------------------------------------
+;; Local variables and constants
+;;----------------------------------------------------------------------------
+
(eval-and-compile
(defconst ses-localvars
'(ses--blank-line ses--cells ses--col-printers ses--col-widths ses--curcell
ses--curcell-overlay ses--default-printer ses--deferred-narrow
ses--deferred-recalc ses--deferred-write ses--file-format
ses--header-hscroll ses--header-row ses--header-string ses--linewidth
- ses--numcols ses--numrows ses--symbolic-formulas
+ ses--numcols ses--numrows ses--symbolic-formulas ses--data-marker
+ ses--params-marker
;;Global variables that we override
mode-line-process next-line-add-newlines transient-mark-mode)
"Buffer-local variables used by SES."))
(make-local-variable x)
(set x nil)))
+(defconst ses-paramlines-plist
+ '(ses--col-widths -5 ses--col-printers -4 ses--default-printer -3
+ ses--header-row -2 ses--file-format 1 ses--numrows 2
+ ses--numcols 3)
+ "Offsets from 'Global parameters' line to various parameter lines in the
+data area of a spreadsheet.")
+
-;;;
-;;; "Side-effect variables". They are set in one function, altered in
-;;; another as a side effect, then read back by the first, as a way of
-;;; passing back more than one value. These declarations are just to make
-;;; the compiler happy, and to conform to standard Emacs-Lisp practice (I
-;;; think the make-local-variable trick above is cleaner).
-;;;
+;;
+;; "Side-effect variables". They are set in one function, altered in
+;; another as a side effect, then read back by the first, as a way of
+;; passing back more than one value. These declarations are just to make
+;; the compiler happy, and to conform to standard Emacs-Lisp practice (I
+;; think the make-local-variable trick above is cleaner).
+;;
(defvar ses-relocate-return nil
"Set by `ses-relocate-formula' and `ses-relocate-range', read by
(defvar ses-call-printer-return nil
"Set to t if last cell printer invoked by `ses-call-printer' requested
left-justification of the result. Set to error-signal if ses-call-printer
-encountered an error during printing. Nil otherwise.")
+encountered an error during printing. nil otherwise.")
(defvar ses-start-time nil
"Time when current operation started. Used by `ses-time-check' to decide
when to emit a progress message.")
-;;;----------------------------------------------------------------------------
-;;;; Macros
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Macros
+;;----------------------------------------------------------------------------
(defmacro ses-get-cell (row col)
"Return the cell structure that stores information about cell (ROW,COL)."
`(aref (aref ses--cells ,row) ,col))
+;; We might want to use defstruct here, but cells are explicitly used as
+;; arrays in ses-set-cell, so we'd need to fix this first. --Stef
+(defsubst ses-make-cell (&optional symbol formula printer references)
+ (vector symbol formula printer references))
+
(defmacro ses-cell-symbol (row &optional col)
"From a CELL or a pair (ROW,COL), get the symbol that names the local-variable holding its value. (0,0) => A1."
`(aref ,(if col `(ses-get-cell ,row ,col) row) 0))
(setq printer `(ses-safe-printer ,printer)))
(aset (aref ses--cells (car rowcol))
(cdr rowcol)
- (vector sym formula printer references)))
+ (ses-make-cell sym formula printer references)))
(set sym value)
sym)
;;print area (excluding the terminating newline)
(setq ses--col-widths widths
ses--linewidth (apply '+ -1 (mapcar '1+ widths))
- ses--blank-line (concat (make-string ses--linewidth ? ) "\n"))
+ ses--blank-line (concat (make-string ses--linewidth ?\s) "\n"))
t)
(defmacro ses-column-printers (printers)
(defmacro ses-header-row (row)
"Load the header row from the spreadsheet file and checks it
for safety. This is a macro to prevent propagate-on-load viruses."
- (or (and (wholenump row) (< row ses--numrows))
+ (or (and (wholenump row) (or (zerop ses--numrows) (< row ses--numrows)))
(error "Bad header-row"))
(setq ses--header-row row)
t)
-(defmacro ses-dotimes-msg (spec msg &rest body)
- "(ses-dotimes-msg (VAR LIMIT) MSG BODY...): Like `dotimes', but
-a message is emitted using MSG every second or so during the loop."
- (let ((msgvar (make-symbol "msg"))
- (limitvar (make-symbol "limit"))
- (var (car spec))
- (limit (cadr spec)))
- `(let ((,limitvar ,limit)
- (,msgvar ,msg))
- (setq ses-start-time (float-time))
- (message ,msgvar)
- (setq ,msgvar (concat ,msgvar " (%d%%)"))
- (dotimes (,var ,limitvar)
- (ses-time-check ,msgvar '(/ (* ,var 100) ,limitvar))
- ,@body)
- (message nil))))
-
-(put 'ses-dotimes-msg 'lisp-indent-function 2)
-(def-edebug-spec ses-dotimes-msg ((symbolp form) form body))
-
(defmacro ses-dorange (curcell &rest body)
"Execute BODY repeatedly, with the variables `row' and `col' set to each
cell in the range specified by CURCELL. The range is available in the
variables `minrow', `maxrow', `mincol', and `maxcol'."
+ (declare (indent defun) (debug (form body)))
(let ((cur (make-symbol "cur"))
(min (make-symbol "min"))
(max (make-symbol "max"))
(setq col (+ ,c mincol))
,@body))))))
-(put 'ses-dorange 'lisp-indent-function 'defun)
-(def-edebug-spec ses-dorange (form body))
-
;;Support for coverage testing.
(defmacro 1value (form)
"For code-coverage testing, indicate that FORM is expected to always have
form)
-;;;----------------------------------------------------------------------------
-;;;; Utility functions
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Utility functions
+;;----------------------------------------------------------------------------
(defun ses-vector-insert (array idx new)
"Create a new vector which is one larger than ARRAY and has NEW inserted
(defun ses-create-cell-variable-range (minrow maxrow mincol maxcol)
"Create buffer-local variables for cells. This is undoable."
- (push `(ses-destroy-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
+ (push `(apply ses-destroy-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
buffer-undo-list)
(let (sym xrow xcol)
(dotimes (row (1+ (- maxrow minrow)))
(put sym 'ses-cell (cons xrow xcol))
(make-local-variable sym)))))
-;;;We do not delete the ses-cell properties for the cell-variables, in case a
-;;;formula that refers to this cell is in the kill-ring and is later pasted
-;;;back in.
+;;We do not delete the ses-cell properties for the cell-variables, in case a
+;;formula that refers to this cell is in the kill-ring and is later pasted
+;;back in.
(defun ses-destroy-cell-variable-range (minrow maxrow mincol maxcol)
"Destroy buffer-local variables for cells. This is undoable."
(let (sym)
(dotimes (col (1+ (- maxcol mincol)))
(setq sym (ses-create-cell-symbol (+ row minrow) (+ col mincol)))
(if (boundp sym)
- (push `(ses-set-with-undo ,sym ,(symbol-value sym))
+ (push `(apply ses-set-with-undo ,sym ,(symbol-value sym))
buffer-undo-list))
(kill-local-variable sym))))
- (push `(ses-create-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
+ (push `(apply ses-create-cell-variable-range ,minrow ,maxrow ,mincol ,maxcol)
buffer-undo-list))
(defun ses-reset-header-string ()
"Flags the header string for update. Upon undo, the header string will be
updated again."
- (push '(ses-reset-header-string) buffer-undo-list)
+ (push '(apply ses-reset-header-string) buffer-undo-list)
(setq ses--header-hscroll -1))
;;Split this code off into a function to avoid coverage-testing difficulties
nil)
-;;;----------------------------------------------------------------------------
-;;;; The cells
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; The cells
+;;----------------------------------------------------------------------------
(defun ses-set-cell (row col field val)
"Install VAL as the contents for field FIELD (named by a quoted symbol) of
the old and FORCE is nil."
(let ((cell (ses-get-cell row col))
formula-error printer-error)
- (let ((symbol (ses-cell-symbol cell))
- (oldval (ses-cell-value cell))
+ (let ((oldval (ses-cell-value cell))
(formula (ses-cell-formula cell))
newval)
(if (eq (car-safe formula) 'ses-safe-formula)
(defun ses-update-cells (list &optional force)
"Recalculate cells in LIST, checking for dependency loops. Prints
progress messages every second. Dependent cells are not recalculated
-if the cell's value is unchanged if FORCE is nil."
+if the cell's value is unchanged and FORCE is nil."
(let ((ses--deferred-recalc list)
(nextlist list)
(pos (point))
(goto-char pos)))
-;;;----------------------------------------------------------------------------
-;;;; The print area
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; The print area
+;;----------------------------------------------------------------------------
(defun ses-in-print-area ()
"Returns t if point is in print area of spreadsheet."
- (eq (get-text-property (point) 'keymap) 'ses-mode-print-map))
+ (<= (point) ses--data-marker))
-;;;We turn off point-motion-hooks and explicitly position the cursor, in case
-;;;the intangible properties have gotten screwed up (e.g., when
-;;;ses-goto-print is called during a recursive ses-print-cell).
+;;We turn off point-motion-hooks and explicitly position the cursor, in case
+;;the intangible properties have gotten screwed up (e.g., when
+;;ses-goto-print is called during a recursive ses-print-cell).
(defun ses-goto-print (row col)
"Move point to print area for cell (ROW,COL)."
(let ((inhibit-point-motion-hooks t))
(error "Need a range"))))
(defun ses-print-cell (row col)
- "Format and print the value of cell (ROW,COL) to the print area, using the
-cell's printer function. If the cell's new print form is too wide, it will
-spill over into the following cell, but will not run off the end of the row
-or overwrite the next non-nil field. Result is nil for normal operation, or
-the error signal if the printer function failed and the cell was formatted
+ "Format and print the value of cell (ROW,COL) to the print area.
+Use the cell's printer function. If the cell's new print form is too wide,
+it will spill over into the following cell, but will not run off the end of the
+row or overwrite the next non-nil field. Result is nil for normal operation,
+or the error signal if the printer function failed and the cell was formatted
with \"%s\". If the cell's value is *skip*, nothing is printed because the
preceding cell has spilled over."
(catch 'ses-print-cell
(cond
((< len width)
;;Fill field to length with spaces
- (setq len (make-string (- width len) ? )
+ (setq len (make-string (- width len) ?\s)
text (if (eq ses-call-printer-return t)
(concat text len)
(concat len text))))
maxcol (1+ maxcol)))
(if (<= len maxwidth)
;;Fill to complete width of all the fields spanned
- (setq text (concat text (make-string (- maxwidth len) ? )))
+ (setq text (concat text (make-string (- maxwidth len) ?\s)))
;;Not enough room to end of line or next non-nil field. Truncate
;;if string or decimal; otherwise fill with error indicator
(setq sig `(error "Too wide" ,text))
lambda of one argument) on VALUE. Result is the the printed cell as a
string. The variable `ses-call-printer-return' is set to t if the printer
used parenthesis to request left-justification, or the error-signal if the
-printer signalled one (and \"%s\" is used as the default printer), else nil."
+printer signaled one (and \"%s\" is used as the default printer), else nil."
(setq ses-call-printer-return nil)
(unless value
(setq value ""))
COL=NUMCOLS. Deletes characters if CHANGE < 0. Caller should bind
inhibit-quit to t."
(let ((inhibit-read-only t)
- (blank (if (> change 0) (make-string change ? )))
+ (blank (if (> change 0) (make-string change ?\s)))
(at-end (= col ses--numcols)))
(ses-set-with-undo 'ses--linewidth (+ ses--linewidth change))
;;ses-set-with-undo always returns t for strings.
(1value (ses-set-with-undo 'ses--blank-line
- (concat (make-string ses--linewidth ? ) "\n")))
+ (concat (make-string ses--linewidth ?\s) "\n")))
(dotimes (row ses--numrows)
(ses-goto-print row col)
(when at-end
(ses-print-cell (car rowcol) (cdr rowcol)))))
-;;;----------------------------------------------------------------------------
-;;;; The data area
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; The data area
+;;----------------------------------------------------------------------------
+
+(defun ses-narrowed-p () (/= (- (point-max) (point-min)) (buffer-size)))
+
+(defun ses-widen ()
+ "Turn off narrowing, to be reenabled at end of command loop."
+ (if (ses-narrowed-p)
+ (setq ses--deferred-narrow t))
+ (widen))
(defun ses-goto-data (def &optional col)
"Move point to data area for (DEF,COL). If DEF is a row
number, COL is the column number for a data cell -- otherwise DEF
is one of the symbols ses--col-widths, ses--col-printers,
ses--default-printer, ses--numrows, or ses--numcols."
- (if (< (point-max) (buffer-size))
- (setq ses--deferred-narrow t))
- (widen)
+ (ses-widen)
(let ((inhibit-point-motion-hooks t)) ;In case intangible attrs are wrong
- (goto-char (point-min))
(if col
- ;;It's a cell
- (forward-line (+ ses--numrows 2 (* def (1+ ses--numcols)) col))
- ;;Convert def-symbol to offset
- (setq def (plist-get ses-paramlines-plist def))
- (or def (signal 'args-out-of-range nil))
- (forward-line (+ (* ses--numrows (+ ses--numcols 2)) def)))))
+ ;;It's a cell
+ (progn
+ (goto-char ses--data-marker)
+ (forward-line (+ 1 (* def (1+ ses--numcols)) col)))
+ ;;Convert def-symbol to offset
+ (setq def (plist-get ses-paramlines-plist def))
+ (or def (signal 'args-out-of-range nil))
+ (goto-char ses--params-marker)
+ (forward-line def))))
(defun ses-set-parameter (def value &optional elem)
- "Sets parameter DEF to VALUE (with undo) and writes the value to the data
-area. See `ses-goto-data' for meaning of DEF. Newlines in the data
-are escaped. If ELEM is specified, it is the array subscript within DEF to
-be set to VALUE."
+ "Set parameter DEF to VALUE (with undo) and write the value to the data area.
+See `ses-goto-data' for meaning of DEF. Newlines in the data are escaped.
+If ELEM is specified, it is the array subscript within DEF to be set to VALUE."
(save-excursion
;;We call ses-goto-data early, using the old values of numrows and
;;numcols in case one of them is being changed.
(ses-goto-data def)
- (if elem
- (ses-aset-with-undo (symbol-value def) elem value)
- (ses-set-with-undo def value))
(let ((inhibit-read-only t)
- (fmt (plist-get '(ses--column-widths "(ses-column-widths %S)"
+ (fmt (plist-get '(ses--col-widths "(ses-column-widths %S)"
ses--col-printers "(ses-column-printers %S)"
ses--default-printer "(ses-default-printer %S)"
ses--header-row "(ses-header-row %S)"
ses--file-format " %S ;SES file-format"
ses--numrows " %S ;numrows"
ses--numcols " %S ;numcols")
- def)))
- (delete-region (point) (line-end-position))
- (insert (format fmt (symbol-value def))))))
+ def))
+ oldval)
+ (if elem
+ (progn
+ (setq oldval (aref (symbol-value def) elem))
+ (aset (symbol-value def) elem value))
+ (setq oldval (symbol-value def))
+ (set def value))
+ ;;Special undo since it's outside the narrowed buffer
+ (let (buffer-undo-list)
+ (delete-region (point) (line-end-position))
+ (insert (format fmt (symbol-value def))))
+ (push `(apply ses-set-parameter ,def ,oldval ,elem) buffer-undo-list))))
+
(defun ses-write-cells ()
- "`ses--deferred-write' is a list of (ROW,COL) for cells to be written from
-buffer-local variables to data area. Newlines in the data are escaped."
+ "Write cells in `ses--deferred-write' from local variables to data area.
+Newlines in the data are escaped."
(let* ((inhibit-read-only t)
(print-escape-newlines t)
rowcol row col cell sym formula printer text)
(message " "))))
-;;;----------------------------------------------------------------------------
-;;;; Formula relocation
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Formula relocation
+;;----------------------------------------------------------------------------
(defun ses-formula-references (formula &optional result-so-far)
"Produce a list of symbols for cells that this formula's value
))))
result-so-far)
+(defsubst ses-relocate-symbol (sym rowcol startrow startcol rowincr colincr)
+ "Relocate one symbol SYM, whichs corresponds to ROWCOL (a cons of ROW and
+COL). Cells starting at (STARTROW,STARTCOL) are being shifted
+by (ROWINCR,COLINCR)."
+ (let ((row (car rowcol))
+ (col (cdr rowcol)))
+ (if (or (< row startrow) (< col startcol))
+ sym
+ (setq row (+ row rowincr)
+ col (+ col colincr))
+ (if (and (>= row startrow) (>= col startcol)
+ (< row ses--numrows) (< col ses--numcols))
+ ;;Relocate this variable
+ (ses-create-cell-symbol row col)
+ ;;Delete reference to a deleted cell
+ nil))))
+
(defun ses-relocate-formula (formula startrow startcol rowincr colincr)
"Produce a copy of FORMULA where all symbols that refer to cells in row
STARTROW or above and col STARTCOL or above are altered by adding ROWINCR
result))))
(nreverse result))))
-(defun ses-relocate-symbol (sym rowcol startrow startcol rowincr colincr)
- "Relocate one symbol SYM, whichs corresponds to ROWCOL (a cons of ROW and
-COL). Cells starting at (STARTROW,STARTCOL) are being shifted
-by (ROWINCR,COLINCR)."
- (let ((row (car rowcol))
- (col (cdr rowcol)))
- (if (or (< row startrow) (< col startcol))
- sym
- (setq row (+ row rowincr)
- col (+ col colincr))
- (if (and (>= row startrow) (>= col startcol)
- (< row ses--numrows) (< col ses--numcols))
- ;;Relocate this variable
- (ses-create-cell-symbol row col)
- ;;Delete reference to a deleted cell
- nil))))
-
(defun ses-relocate-range (range startrow startcol rowincr colincr)
"Relocate one RANGE, of the form '(ses-range min max). Cells starting
at (STARTROW,STARTCOL) are being shifted by (ROWINCR,COLINCR). Result is the
to each symbol."
(let (reform)
(let (mycell newval)
- (ses-dotimes-msg (row ses--numrows) "Relocating formulas..."
+ (dotimes-with-progress-reporter
+ (row ses--numrows) "Relocating formulas..."
(dotimes (col ses--numcols)
(setq ses-relocate-return nil
mycell (ses-get-cell row col)
(cond
((and (<= rowincr 0) (<= colincr 0))
;;Deletion of rows and/or columns
- (ses-dotimes-msg (row (- ses--numrows minrow)) "Relocating variables..."
+ (dotimes-with-progress-reporter
+ (row (- ses--numrows minrow)) "Relocating variables..."
(setq myrow (+ row minrow))
(dotimes (col (- ses--numcols mincol))
(setq mycol (+ col mincol)
(let ((disty (1- ses--numrows))
(distx (1- ses--numcols))
myrow mycol)
- (ses-dotimes-msg (row (- ses--numrows minrow)) "Relocating variables..."
+ (dotimes-with-progress-reporter
+ (row (- ses--numrows minrow)) "Relocating variables..."
(setq myrow (- disty row))
(dotimes (col (- ses--numcols mincol))
(setq mycol (- distx col)
(message nil))))
-;;;----------------------------------------------------------------------------
-;;;; Undo control
-;;;----------------------------------------------------------------------------
-
-(defadvice undo-more (around ses-undo-more activate preactivate)
- "Define a meaning for conses in buffer-undo-list whose car is a symbol
-other than t or nil. To undo these, apply the car--a function--to the
-cdr--its arglist."
- (let ((ses-count (ad-get-arg 0)))
- (catch 'undo
- (dolist (ses-x pending-undo-list)
- (unless ses-x
- ;;End of undo boundary
- (setq ses-count (1- ses-count))
- (if (<= ses-count 0)
- ;;We've seen enough boundaries - stop undoing
- (throw 'undo nil)))
- (and (consp ses-x) (symbolp (car ses-x)) (fboundp (car ses-x))
- ;;Undo using apply
- (apply (car ses-x) (cdr ses-x)))))
- (if (not (eq major-mode 'ses-mode))
- ad-do-it
- ;;Here is some extra code for SES mode.
- (setq ses--deferred-narrow
- (or ses--deferred-narrow (< (point-max) (buffer-size))))
- (widen)
- (condition-case x
- ad-do-it
- (error
- ;;Restore narrow if appropriate
- (ses-command-hook)
- (signal (car x) (cdr x)))))))
+;;----------------------------------------------------------------------------
+;; Undo control
+;;----------------------------------------------------------------------------
(defun ses-begin-change ()
- "For undo, remember current buffer-position before we start changing hidden
-stuff."
+ "For undo, remember point before we start changing hidden stuff."
(let ((inhibit-read-only t))
(insert-and-inherit "X")
(delete-region (1- (point)) (point))))
(defun ses-set-with-undo (sym newval)
"Like set, but undoable. Result is t if value has changed."
- ;;We avoid adding redundant entries to the undo list, but this is
+ ;;We try to avoid adding redundant entries to the undo list, but this is
;;unavoidable for strings because equal ignores text properties and there's
;;no easy way to get the whole property list to see if it's different!
(unless (and (boundp sym)
(equal (symbol-value sym) newval)
(not (stringp newval)))
(push (if (boundp sym)
- `(ses-set-with-undo ,sym ,(symbol-value sym))
- `(ses-unset-with-undo ,sym))
+ `(apply ses-set-with-undo ,sym ,(symbol-value sym))
+ `(apply ses-unset-with-undo ,sym))
buffer-undo-list)
(set sym newval)
t))
(defun ses-unset-with-undo (sym)
"Set SYM to be unbound. This is undoable."
(when (1value (boundp sym)) ;;Always bound, except after a programming error
- (push `(ses-set-with-undo ,sym ,(symbol-value sym)) buffer-undo-list)
+ (push `(apply ses-set-with-undo ,sym ,(symbol-value sym)) buffer-undo-list)
(makunbound sym)))
(defun ses-aset-with-undo (array idx newval)
"Like aset, but undoable. Result is t if element has changed"
(unless (equal (aref array idx) newval)
- (push `(ses-aset-with-undo ,array ,idx ,(aref array idx)) buffer-undo-list)
+ (push `(apply ses-aset-with-undo ,array ,idx ,(aref array idx)) buffer-undo-list)
(aset array idx newval)
t))
-;;;----------------------------------------------------------------------------
-;;;; Startup for major mode
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Startup for major mode
+;;----------------------------------------------------------------------------
(defun ses-load ()
"Parse the current buffer and sets up buffer-local variables. Does not
(widen)
;;Read our global parameters, which should be a 3-element list
(goto-char (point-max))
- (search-backward ";;; Local Variables:\n" nil t)
+ (search-backward ";; Local Variables:\n" nil t)
(backward-list 1)
- (let ((params (condition-case nil (read (current-buffer)) (error nil)))
- sym)
+ (setq ses--params-marker (point-marker))
+ (let ((params (condition-case nil (read (current-buffer)) (error nil))))
(or (and (= (safe-length params) 3)
(numberp (car params))
(numberp (cadr params))
- (> (cadr params) 0)
+ (>= (cadr params) 0)
(numberp (nth 2 params))
(> (nth 2 params) 0))
(error "Invalid SES file"))
(ses-set-parameter 'ses--file-format 2)
(message "Upgrading from SES-1 file format")))
(or (= ses--file-format 2)
- (error "This file needs a newer version of the SES library code."))
+ (error "This file needs a newer version of the SES library code"))
(ses-create-cell-variable-range 0 (1- ses--numrows) 0 (1- ses--numcols))
;;Initialize cell array
(setq ses--cells (make-vector ses--numrows nil))
(forward-line ses--numrows)
(or (looking-at ses-print-data-boundary)
(error "Missing marker between print and data areas"))
- (forward-char (length ses-print-data-boundary))
+ (forward-char 1)
+ (setq ses--data-marker (point-marker))
+ (forward-char (1- (length ses-print-data-boundary)))
;;Initialize printer and symbol lists
(mapc 'ses-printer-record ses-standard-printer-functions)
(setq ses--symbolic-formulas nil)
(put-text-property (point-min) (1+ (point-min)) 'front-sticky t)
;;Create intangible properties, which also indicate which cell the text
;;came from.
- (ses-dotimes-msg (row ses--numrows) "Finding cells..."
+ (dotimes-with-progress-reporter (row ses--numrows) "Finding cells..."
(dotimes (col ses--numcols)
(setq pos end
sym (ses-cell-symbol row col))
(put-text-property pos end 'intangible sym)))
;;Adding these properties did not actually alter the text
(unless was-modified
- (set-buffer-modified-p nil)
+ (restore-buffer-modified-p nil)
(buffer-disable-undo)
(buffer-enable-undo)))
;;Create the underlining overlay. It's impossible for (point) to be 2,
overlay, remove special text properties."
(widen)
(let ((inhibit-read-only t)
- (was-modified (buffer-modified-p))
- end)
+ (was-modified (buffer-modified-p)))
;;Delete read-only, keymap, and intangible properties
(set-text-properties (point-min) (point-max) nil)
;;Delete overlay
(setq ses--deferred-narrow 'ses-mode)
(1value (add-hook 'post-command-hook 'ses-command-hook nil t))
(run-with-idle-timer 0.01 nil 'ses-command-hook)
- (run-hooks 'ses-mode-hook)))
+ (run-mode-hooks 'ses-mode-hook)))
(put 'ses-mode 'mode-class 'special)
(let ((old ses--deferred-recalc))
(setq ses--deferred-recalc nil)
(ses-update-cells old)))
- (if ses--deferred-write
- ;;We don't reset the deferred list before starting -- the most
- ;;likely error is keyboard-quit, and we do want to keep trying
- ;;these writes after a quit.
- (ses-write-cells))
+ (when ses--deferred-write
+ ;;We don't reset the deferred list before starting -- the most
+ ;;likely error is keyboard-quit, and we do want to keep trying
+ ;;these writes after a quit.
+ (ses-write-cells)
+ (push '(apply ses-widen) buffer-undo-list))
(when ses--deferred-narrow
;;We're not allowed to narrow the buffer until after-find-file has
;;read the local variables at the end of the file. Now it's safe to
;;do the narrowing.
- (save-excursion
- (goto-char (point-min))
- (forward-line ses--numrows)
- (narrow-to-region (point-min) (point)))
+ (narrow-to-region (point-min) ses--data-marker)
(setq ses--deferred-narrow nil))
;;Update the modeline
(let ((oldcell ses--curcell))
(error
(unless executing-kbd-macro
(ding))
- (message (error-message-string err))))
+ (message "%s" (error-message-string err))))
nil) ;Make coverage-tester happy
-(defun ses-header-string-left-offset ()
- "Number of characters in left fringe and left scrollbar (if any)."
- (let ((left-fringe (round (or (frame-parameter nil 'left-fringe) 0)
- (frame-char-width)))
- (left-scrollbar (if (not (eq (frame-parameter nil
- 'vertical-scroll-bars)
- 'left))
- 0
- (let ((x (frame-parameter nil 'scroll-bar-width)))
- ;;Non-toolkil bar is always 14 pixels?
- (unless x (setq x 14))
- ;;Always round up
- (ceiling x (frame-char-width))))))
- (+ left-fringe left-scrollbar)))
-
(defun ses-create-header-string ()
- "Sets up `ses--header-string' as the buffer's header line, based on the
-current set of columns and window-scroll position."
- (let* ((left-offset (ses-header-string-left-offset))
- (totwidth (- left-offset (window-hscroll)))
- result width result x)
+ "Set up `ses--header-string' as the buffer's header line.
+Based on the current set of columns and `window-hscroll' position."
+ (let ((totwidth (- (window-hscroll)))
+ result width x)
;;Leave room for the left-side fringe and scrollbar
- (push (make-string left-offset ? ) result)
+ (push (propertize " " 'display '((space :align-to 0))) result)
(dotimes (col ses--numcols)
(setq width (ses-col-width col)
totwidth (+ totwidth width 1))
- (if (= totwidth (+ left-offset 1))
+ (if (= totwidth 1)
;;Scrolled so intercolumn space is leftmost
(push " " result))
- (when (> totwidth (+ left-offset 1))
+ (when (> totwidth 1)
(if (> ses--header-row 0)
(save-excursion
(ses-goto-print (1- ses--header-row) col)
(setq x (buffer-substring-no-properties (point)
(+ (point) width)))
- (if (>= width (- totwidth left-offset))
- (setq x (substring x (- width totwidth left-offset -1))))
- (push (propertize x 'face ses-box-prop) result))
- (setq x (ses-column-letter col))
+ ;; Strip trailing space.
+ (if (string-match "[ \t]+\\'" x)
+ (setq x (substring x 0 (match-beginning 0))))
+ ;; Cut off excess text.
+ (if (>= (length x) totwidth)
+ (setq x (substring x 0 (- totwidth -1)))))
+ (setq x (ses-column-letter col)))
(push (propertize x 'face ses-box-prop) result)
- (push (propertize (make-string (- width (length x)) ?.)
+ (push (propertize "."
'display `((space :align-to ,(1- totwidth)))
'face ses-box-prop)
- result))
+ result)
;;Allow the following space to be squished to make room for the 3-D box
;;Coverage test ignores properties, thinks this is always a space!
(push (1value (propertize " " 'display `((space :align-to ,totwidth))))
(setq ses--header-string (apply 'concat (nreverse result)))))
-;;;----------------------------------------------------------------------------
-;;;; Redisplay and recalculation
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Redisplay and recalculation
+;;----------------------------------------------------------------------------
(defun ses-jump (sym)
"Move point to cell SYM."
;;find the data area when inserting or deleting *skip* values for cells
(dotimes (row ses--numrows)
(insert-and-inherit ses--blank-line))
- (ses-dotimes-msg (row ses--numrows) "Reprinting..."
+ (dotimes-with-progress-reporter (row ses--numrows) "Reprinting..."
(if (eq (ses-cell-value row 0) '*skip*)
;;Column deletion left a dangling skip
(ses-set-cell row 0 'value nil))
(error (setq sig hold))))
(cond
(sig
- (message (error-message-string sig)))
+ (message "%s" (error-message-string sig)))
((consp ses--curcell)
(message " "))
(t
(interactive "*")
(ses-begin-change)
;;Reconstruct reference lists.
- (let (refs x yrow ycol)
+ (let (x yrow ycol)
;;Delete old reference lists
- (ses-dotimes-msg (row ses--numrows) "Deleting references..."
+ (dotimes-with-progress-reporter
+ (row ses--numrows) "Deleting references..."
(dotimes (col ses--numcols)
(ses-set-cell row col 'references nil)))
;;Create new reference lists
- (ses-dotimes-msg (row ses--numrows) "Computing references..."
+ (dotimes-with-progress-reporter
+ (row ses--numrows) "Computing references..."
(dotimes (col ses--numcols)
(dolist (ref (ses-formula-references (ses-cell-formula row col)))
(setq x (ses-sym-rowcol ref)
(cons (ses-cell-symbol row col)
(ses-cell-references yrow ycol)))))))
;;Delete everything and reconstruct basic data area
- (if (< (point-max) (buffer-size))
- (setq ses--deferred-narrow t))
- (widen)
+ (ses-widen)
(let ((inhibit-read-only t))
(goto-char (point-max))
- (if (search-backward ";;; Local Variables:\n" nil t)
+ (if (search-backward ";; Local Variables:\n" nil t)
(delete-region (point-min) (point))
;;Buffer is quite screwed up - can't even save the user-specified locals
(delete-region (point-min) (point-max))
(dotimes (row ses--numrows)
(insert ses--blank-line))
(insert ses-print-data-boundary)
+ (backward-char (1- (length ses-print-data-boundary)))
+ (setq ses--data-marker (point-marker))
+ (forward-char (1- (length ses-print-data-boundary)))
;;Placeholders for cell data
(insert (make-string (* ses--numrows (1+ ses--numcols)) ?\n))
;;Placeholders for col-widths, col-printers, default-printer, header-row
(insert "\n\n\n\n")
- (insert ses-initial-global-parameters))
+ (insert ses-initial-global-parameters)
+ (backward-char (1- (length ses-initial-global-parameters)))
+ (setq ses--params-marker (point-marker))
+ (forward-char (1- (length ses-initial-global-parameters))))
(ses-set-parameter 'ses--col-widths ses--col-widths)
(ses-set-parameter 'ses--col-printers ses--col-printers)
(ses-set-parameter 'ses--default-printer ses--default-printer)
(goto-char (point-min)))
-;;;----------------------------------------------------------------------------
-;;;; Input of cell formulas
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Input of cell formulas
+;;----------------------------------------------------------------------------
(defun ses-edit-cell (row col newval)
"Display current cell contents in minibuffer, for editing. Returns nil if
(defun ses-read-cell (row col newval)
"Self-insert for initial character of cell function."
(interactive
- (let ((initial (this-command-keys))
- (rowcol (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell))))
+ (let* ((initial (this-command-keys))
+ (rowcol (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell)))
+ (curval (ses-cell-formula (car rowcol) (cdr rowcol))))
(barf-if-buffer-read-only)
- (if (string= initial "\"")
- (setq initial "\"\"") ;Enter a string
- (if (string= initial "(")
- (setq initial "()"))) ;Enter a formula list
(list (car rowcol)
(cdr rowcol)
- (read-from-minibuffer (format "Cell %s: " ses--curcell)
- (cons initial 2)
- ses-mode-edit-map
- t ;Convert to Lisp object
- 'ses-read-cell-history))))
+ (read-from-minibuffer
+ (format "Cell %s: " ses--curcell)
+ (cons (if (equal initial "\"") "\"\""
+ (if (equal initial "(") "()" initial)) 2)
+ ses-mode-edit-map
+ t ;Convert to Lisp object
+ 'ses-read-cell-history
+ (prin1-to-string (if (eq (car-safe curval) 'ses-safe-formula)
+ (cadr curval)
+ curval))))))
(when (ses-edit-cell row col newval)
(ses-command-hook) ;Update cell widths before movement
(dolist (x ses-after-entry-functions)
(ses-clear-cell (car rowcol) (cdr rowcol))))))
-;;;----------------------------------------------------------------------------
-;;;; Input of cell-printer functions
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Input of cell-printer functions
+;;----------------------------------------------------------------------------
(defun ses-read-printer (prompt default)
"Common code for `ses-read-cell-printer', `ses-read-column-printer', and `ses-read-default-printer'.
right-justified) or a list of one string (will be left-justified)."
(interactive
(let ((default t)
- prompt x)
+ x)
(ses-check-curcell 'range)
;;Default is none if not all cells in range have same printer
(catch 'ses-read-cell-printer
(ses-reprint-all t)))
-;;;----------------------------------------------------------------------------
-;;;; Spreadsheet size adjustments
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Spreadsheet size adjustments
+;;----------------------------------------------------------------------------
(defun ses-insert-row (count)
"Insert a new row before the current one. With prefix, insert COUNT rows
(ses-set-parameter 'ses--numrows (+ ses--numrows count))
;;Insert each row
(ses-goto-print row 0)
- (ses-dotimes-msg (x count) "Inserting row..."
+ (dotimes-with-progress-reporter (x count) "Inserting row..."
;;Create a row of empty cells. The `symbol' fields will be set by
;;the call to ses-relocate-all.
(setq newrow (make-vector ses--numcols nil))
(dotimes (col ses--numcols)
- (aset newrow col (make-vector ses-cell-size nil)))
+ (aset newrow col (ses-make-cell)))
(setq ses--cells (ses-vector-insert ses--cells row newrow))
- (push `(ses-vector-delete ses--cells ,row 1) buffer-undo-list)
+ (push `(apply ses-vector-delete ses--cells ,row 1) buffer-undo-list)
(insert ses--blank-line))
;;Insert empty lines in cell data area (will be replaced by
;;ses-relocate-all)
(ses-reset-header-string)))
;;Reconstruct text attributes
(ses-setup)
+ ;;Prepare for undo
+ (push '(apply ses-widen) buffer-undo-list)
;;Return to current cell
(if ses--curcell
(ses-jump-safe ses--curcell)
(or (> count 0) (signal 'args-out-of-range nil))
(let ((inhibit-quit t)
(inhibit-read-only t)
- (row (car (ses-sym-rowcol ses--curcell)))
- pos)
+ (row (car (ses-sym-rowcol ses--curcell))))
(setq count (min count (- ses--numrows row)))
(ses-begin-change)
(ses-set-parameter 'ses--numrows (- ses--numrows count))
(ses-reset-header-string)))
;;Reconstruct attributes
(ses-setup)
+ ;;Prepare for undo
+ (push '(apply ses-widen) buffer-undo-list)
(ses-jump-safe ses--curcell))
(defun ses-insert-column (count &optional col width printer)
- "Insert a new column before COL (default is the current one). With prefix,
-insert COUNT columns before current one. If COL is specified, the new
-column(s) get the specified WIDTH and PRINTER (otherwise they're taken from
-the current column)."
+ "Insert a new column before COL (default is the current one).
+With prefix, insert COUNT columns before current one.
+If COL is specified, the new column(s) get the specified WIDTH and PRINTER
+\(otherwise they're taken from the current column)."
(interactive "*p")
(ses-check-curcell)
(or (> count 0) (signal 'args-out-of-range nil))
(ses-create-cell-variable-range 0 (1- ses--numrows)
ses--numcols (+ ses--numcols count -1))
;;Insert each column.
- (ses-dotimes-msg (x count) "Inserting column..."
+ (dotimes-with-progress-reporter (x count) "Inserting column..."
;;Create a column of empty cells. The `symbol' fields will be set by
;;the call to ses-relocate-all.
(ses-adjust-print-width col (1+ width))
(setq has-skip t))
(ses-aset-with-undo ses--cells row
(ses-vector-insert (aref ses--cells row)
- col
- (make-vector ses-cell-size nil)))
+ col (ses-make-cell)))
;;Insert empty lines in cell data area (will be replaced by
;;ses-relocate-all)
(ses-goto-data row col)
(inhibit-read-only t)
(rowcol (ses-sym-rowcol ses--curcell))
(width 0)
- new col origrow has-skip)
+ col origrow has-skip)
(setq origrow (car rowcol)
col (cdr rowcol)
count (min count (- ses--numcols col)))
(ses-begin-change)
(ses-set-parameter 'ses--numcols (- ses--numcols count))
(ses-adjust-print-width col (- width))
- (ses-dotimes-msg (row ses--numrows) "Deleting column..."
+ (dotimes-with-progress-reporter (row ses--numrows) "Deleting column..."
;;Delete lines from cell data area
(ses-goto-data row col)
(ses-delete-line count)
(ses-print-cell-new-width row col))))
-;;;----------------------------------------------------------------------------
-;;;; Cut and paste, import and export
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Cut and paste, import and export
+;;----------------------------------------------------------------------------
(defadvice copy-region-as-kill (around ses-copy-region-as-kill
activate preactivate)
(eq (get-text-property beg 'read-only) 'ses)
(eq (get-text-property (1- end) 'read-only) 'ses)))
ad-do-it ;Normal copy-region-as-kill
- (kill-new (ses-copy-region beg end))))
+ (kill-new (ses-copy-region beg end))
+ (if transient-mark-mode
+ (setq deactivate-mark t))
+ nil))
(defun ses-copy-region (beg end)
"Treat the region as rectangular. Convert the intangible attributes to
(colincr (- (cdr rowcol) (cdr first)))
(pos 0)
myrow mycol x)
- (ses-dotimes-msg (row needrows) "Yanking..."
+ (dotimes-with-progress-reporter (row needrows) "Yanking..."
(setq myrow (+ row (car rowcol)))
(dotimes (col needcols)
(setq mycol (+ col (cdr rowcol))
colbool (> needcols 0))
(when (or rowbool colbool)
;;Need to insert. Get confirm
- (or (y-or-n-p (format "Yank will insert %s%s%s. Continue "
+ (or (y-or-n-p (format "Yank will insert %s%s%s. Continue? "
(if rowbool (format "%d rows" needrows) "")
(if (and rowbool colbool) " and " "")
(if colbool (format "%d columns" needcols) "")))
(kill-new result)))
-;;;----------------------------------------------------------------------------
-;;;; Other user commands
-;;;----------------------------------------------------------------------------
-
-;; This should be used by `call-interactively'.
-(defun ses-read-number (prompt &optional default)
- (let ((n nil))
- (when default
- (setq prompt
- (if (string-match "\\(\\):[^:]*" prompt)
- (replace-match (format " [%s]" default) t t prompt 1)
- (concat prompt (format " [%s] " default)))))
- (while
- (progn
- (let ((str (read-from-minibuffer prompt nil nil nil nil
- (number-to-string default))))
- (setq n (cond
- ((zerop (length str)) default)
- ((stringp str) (read str)))))
- (unless (numberp n)
- (message "Please enter a number.")
- (sit-for 1)
- t)))
- n))
+;;----------------------------------------------------------------------------
+;; Other user commands
+;;----------------------------------------------------------------------------
(defun ses-unset-header-row ()
"Select the default header row."
(list (if (numberp current-prefix-arg) current-prefix-arg
(let ((currow (1+ (car (ses-sym-rowcol ses--curcell)))))
(if current-prefix-arg
- (ses-read-number "Header row: " currow)
+ (read-number "Header row: " currow)
currow)))))
(if (or (< row 0) (> row ses--numrows))
(error "Invalid header-row"))
(ses-begin-change)
- (ses-set-parameter 'ses--header-row row)
+ (let ((oldval ses--header-row))
+ (let (buffer-undo-list)
+ (ses-set-parameter 'ses--header-row row))
+ (push `(apply ses-set-header-row ,oldval) buffer-undo-list))
(ses-reset-header-string))
(defun ses-mark-row ()
(ses-insert-ses-range))
-;;;----------------------------------------------------------------------------
-;;;; Checking formulas for safety
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Checking formulas for safety
+;;----------------------------------------------------------------------------
(defun ses-safe-printer (printer)
"Returns PRINTER if safe, or the substitute printer `ses-unsafe' otherwise."
formula checker)))))
-;;;----------------------------------------------------------------------------
-;;;; Standard formulas
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Standard formulas
+;;----------------------------------------------------------------------------
(defmacro ses-range (from to)
"Expands to a list of cell-symbols for the range. The range automatically
"Return ARGS reversed, with the blank elements (nil and *skip*) removed."
(let (result)
(dolist (cur args)
- (and cur (not (eq cur '*skip*))
- (push cur result)))
+ (unless (memq cur '(nil *skip*))
+ (push cur result)))
result))
(defun ses+ (&rest args)
(cons 'list result)))
;;All standard formulas are safe
-(dolist (x '(ses-range ses-delete-blanks ses+ ses-average ses-select))
+(dolist (x '(ses-cell-value ses-range ses-delete-blanks ses+ ses-average
+ ses-select))
(put x 'side-effect-free t))
-;;;----------------------------------------------------------------------------
-;;;; Standard print functions
-;;;----------------------------------------------------------------------------
+;;----------------------------------------------------------------------------
+;; Standard print functions
+;;----------------------------------------------------------------------------
;;These functions use the variables 'row' and 'col' that are
;;dynamically bound by ses-print-cell. We define these varables at
(let ((printer (or (ses-col-printer col) ses--default-printer))
(width (ses-col-width col))
half)
- (or fill (setq fill ? ))
+ (or fill (setq fill ?\s))
(or span (setq span 0))
(setq value (ses-call-printer printer value))
(dotimes (x span)
(provide 'ses)
-;;; arch-tag: 88c1ccf0-4293-4824-8c5d-0757b52217f3
-;; ses.el ends here.
+;; arch-tag: 88c1ccf0-4293-4824-8c5d-0757b52217f3
+;;; ses.el ends here