aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2014-06-15 00:10:40 -0400
committerStefan Monnier2014-06-15 00:10:40 -0400
commitdf5703a00d610a89fa6bc1da906228907b36b5d8 (patch)
tree037d32ed58ad5c713baa21d64632c94c5d4a7839
parente52868b16f33eb31cbe912f1ebc98136c5743238 (diff)
downloademacs-df5703a00d610a89fa6bc1da906228907b36b5d8.tar.gz
emacs-df5703a00d610a89fa6bc1da906228907b36b5d8.zip
* lisp/ses.el: Miscellaneous cleanups; use lexical-binding; avoid add-to-list.
(ses-localvars): Remove ses--local-printer-list, unused. (ses--metaprogramming): New macro. Use it to defvar variables. (ses-set-localvars): Simplify. (ses--locprn, ses-cell): Use defstruct. Change ses-cell's property-list into an alist. (ses-locprn-get-compiled, ses-locprn-compiled-aset) (ses-locprn-get-def, ses-locprn-def-aset, ses-locprn-get-number): Remove; use defstruct accessors/setters instead. (ses-cell-formula-aset, ses-cell-printer-aset) (ses-cell-references-aset): Remove, use setf instead. (ses--alist-get): New function. (ses-cell-property): Rename from ses-cell-property-get and rewrite. Use an alist instead of a plist and don't do move-to-front since the list is always short. (ses-cell-property-get-fun, ses-cell-property-delq-fun) (ses-cell-property-set-fun, ses-cell-property-set) (ses-cell-property-pop-fun, ses-cell-property-get-handle) (ses-cell-property-handle-car, ses-cell-property-handle-setcar): Remove. (ses--letref): New macro. (ses-cell-property-pop): Rewrite. (ses--cell): Rename from ses-cell and make it into a function. Make `formula' fallback on `value' if nil. (ses--local-printer): Rename from ses-local-printer and make it into a function. (ses-set-cell): Turn it into a macro so finding the accessor from the field name is done at compile time. (ses-repair-cell-reference-all): Test presence of `sym' rather than `ref' before adding `sym' to :ses-repair-reference. (ses-calculate-cell): Use ses--letref rather than ses-cell-property-get-handle. (ses-write-cells): Use a single prin1-to-string. (ses-setter-with-undo): New function. (ses-aset-with-undo, ses-set-with-undo): Rewrite using it. (ses-unset-with-undo): Remove. (ses-load): Prefer apply' over `eval'. (ses-read-printer, ses-set-column-width): Use standard "(default foo)" format.
-rw-r--r--lisp/ChangeLog42
-rw-r--r--lisp/ses.el742
2 files changed, 375 insertions, 409 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 8cb58bde25c..c243c6ea3ef 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,45 @@
12014-06-15 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * ses.el: Miscellaneous cleanups; use lexical-binding; avoid
4 add-to-list.
5 (ses-localvars): Remove ses--local-printer-list, unused.
6 (ses--metaprogramming): New macro. Use it to defvar variables.
7 (ses-set-localvars): Simplify.
8 (ses--locprn, ses-cell): Use defstruct. Change ses-cell's
9 property-list into an alist.
10 (ses-locprn-get-compiled, ses-locprn-compiled-aset)
11 (ses-locprn-get-def, ses-locprn-def-aset, ses-locprn-get-number):
12 Remove; use defstruct accessors/setters instead.
13 (ses-cell-formula-aset, ses-cell-printer-aset)
14 (ses-cell-references-aset): Remove, use setf instead.
15 (ses--alist-get): New function.
16 (ses-cell-property): Rename from ses-cell-property-get and rewrite.
17 Use an alist instead of a plist and don't do move-to-front since the
18 list is always short.
19 (ses-cell-property-get-fun, ses-cell-property-delq-fun)
20 (ses-cell-property-set-fun, ses-cell-property-set)
21 (ses-cell-property-pop-fun, ses-cell-property-get-handle)
22 (ses-cell-property-handle-car, ses-cell-property-handle-setcar): Remove.
23 (ses--letref): New macro.
24 (ses-cell-property-pop): Rewrite.
25 (ses--cell): Rename from ses-cell and make it into a function.
26 Make `formula' fallback on `value' if nil.
27 (ses--local-printer): Rename from ses-local-printer and make it into
28 a function.
29 (ses-set-cell): Turn it into a macro so finding the accessor from the
30 field name is done at compile time.
31 (ses-repair-cell-reference-all): Test presence of `sym' rather than
32 `ref' before adding `sym' to :ses-repair-reference.
33 (ses-calculate-cell): Use ses--letref rather than
34 ses-cell-property-get-handle.
35 (ses-write-cells): Use a single prin1-to-string.
36 (ses-setter-with-undo): New function.
37 (ses-aset-with-undo, ses-set-with-undo): Rewrite using it.
38 (ses-unset-with-undo): Remove.
39 (ses-load): Prefer apply' over `eval'.
40 (ses-read-printer, ses-set-column-width): Use standard "(default
41 foo)" format.
42
12014-06-15 Glenn Morris <rgm@gnu.org> 432014-06-15 Glenn Morris <rgm@gnu.org>
2 44
3 * Makefile.in (leim, semantic): Use `make -C' rather than `cd && make'. 45 * Makefile.in (leim, semantic): Use `make -C' rather than `cd && make'.
diff --git a/lisp/ses.el b/lisp/ses.el
index c7c39e0a5eb..a4f5609575d 100644
--- a/lisp/ses.el
+++ b/lisp/ses.el
@@ -1,4 +1,4 @@
1;;; ses.el -- Simple Emacs Spreadsheet -*- coding: utf-8 -*- 1;;; ses.el -- Simple Emacs Spreadsheet -*- lexical-binding:t -*-
2 2
3;; Copyright (C) 2002-2014 Free Software Foundation, Inc. 3;; Copyright (C) 2002-2014 Free Software Foundation, Inc.
4 4
@@ -282,10 +282,6 @@ default printer and then modify its output.")
282 ses--col-widths ses--curcell ses--curcell-overlay 282 ses--col-widths ses--curcell ses--curcell-overlay
283 ses--default-printer 283 ses--default-printer
284 (ses--local-printer-hashmap . :hashmap) 284 (ses--local-printer-hashmap . :hashmap)
285 ;; the list is there to remember the order of local printers like there
286 ;; are written to the SES filen which service the hashmap does not
287 ;; provide.
288 ses--local-printer-list
289 (ses--numlocprn . 0); count of local printers 285 (ses--numlocprn . 0); count of local printers
290 ses--deferred-narrow ses--deferred-recalc 286 ses--deferred-narrow ses--deferred-recalc
291 ses--deferred-write ses--file-format 287 ses--deferred-write ses--file-format
@@ -300,8 +296,12 @@ default printer and then modify its output.")
300 ses--renamed-cell-symb-list 296 ses--renamed-cell-symb-list
301 ;; Global variables that we override 297 ;; Global variables that we override
302 mode-line-process next-line-add-newlines transient-mark-mode) 298 mode-line-process next-line-add-newlines transient-mark-mode)
303 "Buffer-local variables used by SES.") 299 "Buffer-local variables used by SES."))
304 300
301(defmacro ses--metaprogramming (exp) (declare (debug t)) (eval exp t))
302(ses--metaprogramming
303 `(progn ,@(mapcar (lambda (x) `(defvar ,(or (car-safe x) x))) ses-localvars)))
304
305(defun ses-set-localvars () 305(defun ses-set-localvars ()
306 "Set buffer-local and initialize some SES variables." 306 "Set buffer-local and initialize some SES variables."
307 (dolist (x ses-localvars) 307 (dolist (x ses-localvars)
@@ -313,20 +313,10 @@ default printer and then modify its output.")
313 ((integerp (cdr x)) 313 ((integerp (cdr x))
314 (set (make-local-variable (car x)) (cdr x))) 314 (set (make-local-variable (car x)) (cdr x)))
315 ((eq (cdr x) :hashmap) 315 ((eq (cdr x) :hashmap)
316 (set (make-local-variable (car x)) 316 (set (make-local-variable (car x)) (make-hash-table :test 'eq)))
317 (if (boundp (car x))
318 (let ((xv (symbol-value (car x))))
319 (if (hash-table-p xv)
320 (clrhash xv)
321 (warn "Unexpected value of symbol %S, should be a hash table" x)
322 (make-hash-table :test 'eq)))
323 (make-hash-table :test 'eq))))
324 (t (error "Unexpected initializer `%S' in list `ses-localvars' for entry %S" 317 (t (error "Unexpected initializer `%S' in list `ses-localvars' for entry %S"
325 (cdr x) (car x)) ) )) 318 (cdr x) (car x)) ) ))
326 (t (error "Unexpected elements `%S' in list `ses-localvars'" x)))))) 319 (t (error "Unexpected elements `%S' in list `ses-localvars'" x)))))
327
328(eval-when-compile ; silence compiler
329 (ses-set-localvars))
330 320
331;;; This variable is documented as being permitted in file-locals: 321;;; This variable is documented as being permitted in file-locals:
332(put 'ses--symbolic-formulas 'safe-local-variable 'consp) 322(put 'ses--symbolic-formulas 'safe-local-variable 'consp)
@@ -381,186 +371,115 @@ when to emit a progress message.")
381 371
382(defmacro ses-get-cell (row col) 372(defmacro ses-get-cell (row col)
383 "Return the cell structure that stores information about cell (ROW,COL)." 373 "Return the cell structure that stores information about cell (ROW,COL)."
374 (declare (debug t))
384 `(aref (aref ses--cells ,row) ,col)) 375 `(aref (aref ses--cells ,row) ,col))
385 376
386;; We might want to use defstruct here, but cells are explicitly used as 377(cl-defstruct (ses-cell
387;; arrays in ses-set-cell, so we'd need to fix this first. --Stef 378 (:constructor nil)
388(defsubst ses-make-cell (&optional symbol formula printer references 379 (:constructor ses-make-cell
389 property-list) 380 (&optional symbol formula printer references))
390 (vector symbol formula printer references property-list)) 381 (:copier nil)
391 382 ;; This is treated as an 4-elem array in various places.
392(defsubst ses-make-local-printer-info (def &optional compiled-def number) 383 ;; Mostly in ses-set-cell.
393 (let ((v (vector def 384 (:type vector) ;Not named.
394 (or compiled-def (ses-local-printer-compile def)) 385 (:conc-name ses-cell--))
395 (or number ses--numlocprn) 386 symbol formula printer references properties)
396 nil))) 387
397 (push v ses--local-printer-list) 388(cl-defstruct (ses--locprn
398 (aset v 3 ses--local-printer-list) 389 (:constructor)
399 v)) 390 (:constructor ses-make-local-printer-info
400 391 (def &optional (compiled (ses-local-printer-compile def))
401(defmacro ses-locprn-get-compiled (locprn) 392 (number ses--numlocprn))))
402 `(aref ,locprn 1)) 393 def
403 394 compiled
404(defmacro ses-locprn-compiled-aset (locprn compiled) 395 number
405 `(aset ,locprn 1 ,compiled)) 396 local-printer-list)
406
407(defmacro ses-locprn-get-def (locprn)
408 `(aref ,locprn 0))
409
410(defmacro ses-locprn-def-aset (locprn def)
411 `(aset ,locprn 0 ,def))
412
413(defmacro ses-locprn-get-number (locprn)
414 `(aref ,locprn 2))
415 397
416(defmacro ses-cell-symbol (row &optional col) 398(defmacro ses-cell-symbol (row &optional col)
417 "From a CELL or a pair (ROW,COL), get the symbol that names the local-variable holding its value. (0,0) => A1." 399 "From a CELL or a pair (ROW,COL), get the symbol that names the local-variable holding its value. (0,0) => A1."
418 `(aref ,(if col `(ses-get-cell ,row ,col) row) 0)) 400 (declare (debug t))
401 `(ses-cell--symbol ,(if col `(ses-get-cell ,row ,col) row)))
419(put 'ses-cell-symbol 'safe-function t) 402(put 'ses-cell-symbol 'safe-function t)
420 403
421(defmacro ses-cell-formula (row &optional col) 404(defmacro ses-cell-formula (row &optional col)
422 "From a CELL or a pair (ROW,COL), get the function that computes its value." 405 "From a CELL or a pair (ROW,COL), get the function that computes its value."
423 `(aref ,(if col `(ses-get-cell ,row ,col) row) 1)) 406 (declare (debug t))
424 407 `(ses-cell--formula ,(if col `(ses-get-cell ,row ,col) row)))
425(defmacro ses-cell-formula-aset (cell formula)
426 "From a CELL set the function that computes its value."
427 `(aset ,cell 1 ,formula))
428 408
429(defmacro ses-cell-printer (row &optional col) 409(defmacro ses-cell-printer (row &optional col)
430 "From a CELL or a pair (ROW,COL), get the function that prints its value." 410 "From a CELL or a pair (ROW,COL), get the function that prints its value."
431 `(aref ,(if col `(ses-get-cell ,row ,col) row) 2)) 411 (declare (debug t))
432 412 `(ses-cell--printer ,(if col `(ses-get-cell ,row ,col) row)))
433(defmacro ses-cell-printer-aset (cell printer)
434 "From a CELL set the printer that prints its value."
435 `(aset ,cell 2 ,printer))
436 413
437(defmacro ses-cell-references (row &optional col) 414(defmacro ses-cell-references (row &optional col)
438 "From a CELL or a pair (ROW,COL), get the list of symbols for cells whose 415 "From a CELL or a pair (ROW,COL), get the list of symbols for cells whose
439functions refer to its value." 416functions refer to its value."
440 `(aref ,(if col `(ses-get-cell ,row ,col) row) 3)) 417 (declare (debug t))
441 418 `(ses-cell--references ,(if col `(ses-get-cell ,row ,col) row)))
442(defmacro ses-cell-references-aset (cell references)
443 "From a CELL set the list REFERENCES of symbols for cells the
444function of which refer to its value."
445 `(aset ,cell 3 ,references))
446 419
447(defun ses-cell-p (cell) 420(defun ses-cell-p (cell)
448 "Return non `nil' is CELL is a cell of current buffer." 421 "Return non-nil if CELL is a cell of current buffer."
449 (and (vectorp cell) 422 (and (vectorp cell)
450 (= (length cell) 5) 423 (= (length cell) 5)
451 (eq cell (let ((rowcol (ses-sym-rowcol (ses-cell-symbol cell)))) 424 (eq cell (let ((rowcol (ses-sym-rowcol (ses-cell-symbol cell))))
452 (and (consp rowcol) 425 (and (consp rowcol)
453 (ses-get-cell (car rowcol) (cdr rowcol))))))) 426 (ses-get-cell (car rowcol) (cdr rowcol)))))))
454 427
455(defun ses-cell-property-get-fun (property-name cell) 428
456 ;; To speed up property fetching, each time a property is found it is placed 429(defun ses--alist-get (key alist &optional remove)
457 ;; in the first position. This way, after the first get, the full property 430 "Get the value associated to KEY in ALIST."
458 ;; list needs to be scanned only when the property does not exist for that 431 (declare
459 ;; cell. 432 (gv-expander
460 (let* ((plist (aref cell 4)) 433 (lambda (do)
461 (ret (plist-member plist property-name))) 434 (macroexp-let2 macroexp-copyable-p k key
462 (if ret 435 (gv-letplace (getter setter) alist
463 ;; Property was found. 436 (macroexp-let2 nil p `(assq ,k ,getter)
464 (let ((val (cadr ret))) 437 (funcall do `(cdr ,p)
465 (if (eq ret plist) 438 (lambda (v)
466 ;; Property found is already in the first position, so just return 439 (let ((set-exp
467 ;; its value. 440 `(if ,p (setcdr ,p ,v)
468 val 441 ,(funcall setter
469 ;; Property is not in the first position, the following will move it 442 `(cons (setq ,p (cons ,k ,v))
470 ;; there before returning its value. 443 ,getter)))))
471 (let ((next (cddr ret))) 444 (cond
472 (if next 445 ((null remove) set-exp)
473 (progn 446 ((null v)
474 (setcdr ret (cdr next)) 447 `(if ,p ,(funcall setter `(delq ,p ,getter))))
475 (setcar ret (car next))) 448 (t
476 (setcdr (last plist 1) nil))) 449 `(cond
477 (aset cell 4 450 (,v ,set-exp)
478 `(,property-name ,val ,@plist)) 451 (,p ,(funcall setter
479 val))))) 452 `(delq ,p ,getter)))))))))))))))
480 453 (ignore remove) ;;Silence byte-compiler.
481(defmacro ses-cell-property-get (property-name row &optional col) 454 (cdr (assoc key alist)))
482 "Get property named PROPERTY-NAME from a CELL or a pair (ROW,COL). 455
456(defmacro ses--letref (vars place &rest body)
457 (declare (indent 2) (debug (sexp form &rest body)))
458 (gv-letplace (getter setter) place
459 `(cl-macrolet ((,(nth 0 vars) () ',getter)
460 (,(nth 1 vars) (v) (funcall ,setter v)))
461 ,@body)))
462
463(defmacro ses-cell-property (property-name row &optional col)
464 "Get property named PROPERTY-NAME from a CELL or a pair (ROW,COL).
483 465
484When COL is omitted, CELL=ROW is a cell object. When COL is 466When COL is omitted, CELL=ROW is a cell object. When COL is
485present ROW and COL are the integer coordinates of the cell of 467present ROW and COL are the integer coordinates of the cell of
486interest." 468interest."
487 (declare (debug t)) 469 (declare (debug t))
488 `(ses-cell-property-get-fun 470 `(ses--alist-get ,property-name
489 ,property-name 471 (ses-cell--properties
490 ,(if col `(ses-get-cell ,row ,col) row))) 472 ,(if col `(ses-get-cell ,row ,col) row))))
491
492(defun ses-cell-property-delq-fun (property-name cell)
493 (let ((ret (plist-get (aref cell 4) property-name)))
494 (if ret
495 (setcdr ret (cddr ret)))))
496
497(defun ses-cell-property-set-fun (property-name property-val cell)
498 (let* ((plist (aref cell 4))
499 (ret (plist-member plist property-name)))
500 (if ret
501 (setcar (cdr ret) property-val)
502 (aset cell 4 `(,property-name ,property-val ,@plist)))))
503
504(defmacro ses-cell-property-set (property-name property-value row &optional col)
505 "From a CELL or a pair (ROW,COL), set the property value of
506the corresponding cell with name PROPERTY-NAME to PROPERTY-VALUE."
507 (if property-value
508 `(ses-cell-property-set-fun ,property-name ,property-value
509 ,(if col `(ses-get-cell ,row ,col) row))
510 `(ses-cell-property-delq-fun ,property-name
511 ,(if col `(ses-get-cell ,row ,col) row))))
512
513(defun ses-cell-property-pop-fun (property-name cell)
514 (let* ((plist (aref cell 4))
515 (ret (plist-member plist property-name)))
516 (if ret
517 (prog1 (cadr ret)
518 (let ((next (cddr ret)))
519 (if next
520 (progn
521 (setcdr ret (cdr next))
522 (setcar ret (car next)))
523 (if (eq plist ret)
524 (aset cell 4 nil)
525 (setcdr (last plist 2) nil))))))))
526
527 473
528(defmacro ses-cell-property-pop (property-name row &optional col) 474(defmacro ses-cell-property-pop (property-name row &optional col)
529 "From a CELL or a pair (ROW,COL), get and remove the property value of 475 "From a CELL or a pair (ROW,COL), get and remove the property value of
530the corresponding cell with name PROPERTY-NAME." 476the corresponding cell with name PROPERTY-NAME."
531 `(ses-cell-property-pop-fun ,property-name 477 `(ses--letref (pget pset)
532 ,(if col `(ses-get-cell ,row ,col) row))) 478 (ses--alist-get ,property-name
533 479 (ses-cell--properties
534(defun ses-cell-property-get-handle-fun (property-name cell) 480 ,(if col `(ses-get-cell ,row ,col) row))
535 (let* ((plist (aref cell 4)) 481 t)
536 (ret (plist-member plist property-name))) 482 (prog1 (pget) (pset nil))))
537 (if ret
538 (if (eq ret plist)
539 (cdr ret)
540 (let ((val (cadr ret))
541 (next (cddr ret)))
542 (if next
543 (progn
544 (setcdr ret (cdr next))
545 (setcar ret (car next)))
546 (setcdr (last plist 2) nil))
547 (setq ret (cons val plist))
548 (aset cell 4 (cons property-name ret))
549 ret))
550 (setq ret (cons nil plist))
551 (aset cell 4 (cons property-name ret))
552 ret)))
553
554(defmacro ses-cell-property-get-handle (property-name row &optional col)
555 "From a CELL or a pair (ROW,COL), get a cons cell whose car is
556the property value of the corresponding cell property with name
557PROPERTY-NAME."
558 `(ses-cell-property-get-handle-fun ,property-name
559 ,(if col `(ses-get-cell ,row ,col) row)))
560
561
562(defalias 'ses-cell-property-handle-car 'car)
563(defalias 'ses-cell-property-handle-setcar 'setcar)
564 483
565(defmacro ses-cell-value (row &optional col) 484(defmacro ses-cell-value (row &optional col)
566 "From a CELL or a pair (ROW,COL), get the current value for that cell." 485 "From a CELL or a pair (ROW,COL), get the current value for that cell."
@@ -592,14 +511,14 @@ is nil if SYM is not a symbol that names a cell."
592 (< (cdr rowcol) ses--numcols) 511 (< (cdr rowcol) ses--numcols)
593 (eq (ses-cell-symbol (car rowcol) (cdr rowcol)) sym)))))) 512 (eq (ses-cell-symbol (car rowcol) (cdr rowcol)) sym))))))
594 513
595(defmacro ses-cell (sym value formula printer references) 514(defun ses--cell (sym value formula printer references)
596 "Load a cell SYM from the spreadsheet file. Does not recompute VALUE from 515 "Load a cell SYM from the spreadsheet file. Does not recompute VALUE from
597FORMULA, does not reprint using PRINTER, does not check REFERENCES. This is a 516FORMULA, does not reprint using PRINTER, does not check REFERENCES.
598macro to prevent propagate-on-load viruses. Safety-checking for FORMULA and 517Safety-checking for FORMULA and PRINTER are deferred until first use."
599PRINTER are deferred until first use."
600 (let ((rowcol (ses-sym-rowcol sym))) 518 (let ((rowcol (ses-sym-rowcol sym)))
601 (ses-formula-record formula) 519 (ses-formula-record formula)
602 (ses-printer-record printer) 520 (ses-printer-record printer)
521 (unless formula (setq formula value))
603 (or (atom formula) 522 (or (atom formula)
604 (eq safe-functions t) 523 (eq safe-functions t)
605 (setq formula `(ses-safe-formula ,formula))) 524 (setq formula `(ses-safe-formula ,formula)))
@@ -607,11 +526,9 @@ PRINTER are deferred until first use."
607 (stringp printer) 526 (stringp printer)
608 (eq safe-functions t) 527 (eq safe-functions t)
609 (setq printer `(ses-safe-printer ,printer))) 528 (setq printer `(ses-safe-printer ,printer)))
610 (aset (aref ses--cells (car rowcol)) 529 (setf (ses-get-cell (car rowcol) (cdr rowcol))
611 (cdr rowcol)
612 (ses-make-cell sym formula printer references))) 530 (ses-make-cell sym formula printer references)))
613 (set sym value) 531 (set sym value))
614 sym)
615 532
616(defun ses-local-printer-compile (printer) 533(defun ses-local-printer-compile (printer)
617 "Convert local printer function into faster printer 534 "Convert local printer function into faster printer
@@ -622,18 +539,18 @@ definition."
622 `(lambda (x) (format ,printer x))) 539 `(lambda (x) (format ,printer x)))
623 (t (error "Invalid printer %S" printer)))) 540 (t (error "Invalid printer %S" printer))))
624 541
625(defmacro ses-local-printer (printer-name printer-def) 542(defun ses--local-printer (name def)
626 "Define a local printer with name PRINTER-NAME and definition 543 "Define a local printer with name NAME and definition DEF.
627PRINTER-DEF. Return the printer info." 544Return the printer info."
628 (or 545 (or
629 (and (symbolp printer-name) 546 (and (symbolp name)
630 (ses-printer-validate printer-def)) 547 (ses-printer-validate def))
631 (error "Invalid local printer definition")) 548 (error "Invalid local printer definition"))
632 (and (gethash printer-name ses--local-printer-hashmap) 549 (and (gethash name ses--local-printer-hashmap)
633 (error "Duplicate printer definition %S" printer-name)) 550 (error "Duplicate printer definition %S" name))
634 (add-to-list 'ses-read-printer-history (symbol-name printer-name)) 551 (add-to-list 'ses-read-printer-history (symbol-name name))
635 (puthash printer-name 552 (puthash name
636 (ses-make-local-printer-info (ses-safe-printer printer-def)) 553 (ses-make-local-printer-info (ses-safe-printer def))
637 ses--local-printer-hashmap)) 554 ses--local-printer-hashmap))
638 555
639(defmacro ses-column-widths (widths) 556(defmacro ses-column-widths (widths)
@@ -704,9 +621,11 @@ variables `minrow', `maxrow', `mincol', and `maxcol'."
704(defmacro 1value (form) 621(defmacro 1value (form)
705 "For code-coverage testing, indicate that FORM is expected to always have 622 "For code-coverage testing, indicate that FORM is expected to always have
706the same value." 623the same value."
624 (declare (debug t))
707 form) 625 form)
708(defmacro noreturn (form) 626(defmacro noreturn (form)
709 "For code-coverage testing, indicate that FORM will always signal an error." 627 "For code-coverage testing, indicate that FORM will always signal an error."
628 (declare (debug t))
710 form) 629 form)
711 630
712 631
@@ -753,7 +672,7 @@ is a vector--if a symbol, the new vector is assigned as the symbol's value."
753 (and (symbolp printer) (gethash printer ses--local-printer-hashmap)) 672 (and (symbolp printer) (gethash printer ses--local-printer-hashmap))
754 (functionp printer) 673 (functionp printer)
755 (and (stringp (car-safe printer)) (not (cdr printer))) 674 (and (stringp (car-safe printer)) (not (cdr printer)))
756 (error "Invalid printer function")) 675 (error "Invalid printer function %S" printer))
757 printer) 676 printer)
758 677
759(defun ses-printer-record (printer) 678(defun ses-printer-record (printer)
@@ -785,20 +704,22 @@ for this spreadsheet."
785 (intern (concat (ses-column-letter col) (number-to-string (1+ row))))) 704 (intern (concat (ses-column-letter col) (number-to-string (1+ row)))))
786 705
787(defun ses-decode-cell-symbol (str) 706(defun ses-decode-cell-symbol (str)
788 "Decode a symbol \"A1\" => (0,0). Returns `nil' if STR is not a 707 "Decode a symbol \"A1\" => (0,0). Return nil if STR is not a
789 canonical cell name. Does not save match data." 708canonical cell name."
790 (let (case-fold-search) 709 (let (case-fold-search)
791 (and (string-match "\\`\\([A-Z]+\\)\\([0-9]+\\)\\'" str) 710 (and (string-match "\\`\\([A-Z]+\\)\\([0-9]+\\)\\'" str)
792 (let* ((col-str (match-string-no-properties 1 str)) 711 (let* ((col-str (match-string-no-properties 1 str))
793 (col 0) 712 (col 0)
794 (col-base 1) 713 (col-base 1)
795 (col-idx (1- (length col-str))) 714 (col-idx (1- (length col-str)))
796 (row (1- (string-to-number (match-string-no-properties 2 str))))) 715 (row (1- (string-to-number
716 (match-string-no-properties 2 str)))))
797 (and (>= row 0) 717 (and (>= row 0)
798 (progn 718 (progn
799 (while 719 (while
800 (progn 720 (progn
801 (setq col (+ col (* (- (aref col-str col-idx) ?A) col-base)) 721 (setq col (+ col (* (- (aref col-str col-idx) ?A)
722 col-base))
802 col-base (* col-base 26) 723 col-base (* col-base 26)
803 col-idx (1- col-idx)) 724 col-idx (1- col-idx))
804 (and (>= col-idx 0) 725 (and (>= col-idx 0)
@@ -872,21 +793,34 @@ and (eval ARG) and reset `ses-start-time' to the current time."
872;; The cells 793;; The cells
873;;---------------------------------------------------------------------------- 794;;----------------------------------------------------------------------------
874 795
875(defun ses-set-cell (row col field val) 796(defmacro ses-set-cell (row col field val)
876 "Install VAL as the contents for field FIELD (named by a quoted symbol) of 797 "Install VAL as the contents for field FIELD (named by a quoted symbol) of
877cell (ROW,COL). This is undoable. The cell's data will be updated through 798cell (ROW,COL). This is undoable. The cell's data will be updated through
878`post-command-hook'." 799`post-command-hook'."
879 (let ((cell (ses-get-cell row col)) 800 `(let ((row ,row)
880 (elt (plist-get '(value t symbol 0 formula 1 printer 2 references 3) 801 (col ,col)
881 field)) 802 (val ,val))
882 change) 803 (let* ((cell (ses-get-cell row col))
883 (or elt (signal 'args-out-of-range nil)) 804 (change
884 (setq change (if (eq elt t) 805 ,(let ((field (eval field t)))
885 (ses-set-with-undo (ses-cell-symbol cell) val) 806 (if (eq field 'value)
886 (ses-aset-with-undo cell elt val))) 807 `(ses-set-with-undo (ses-cell-symbol cell) val)
887 (if change 808 ;; (let* ((slots (get 'ses-cell 'cl-struct-slots))
888 (add-to-list 'ses--deferred-write (cons row col)))) 809 ;; (slot (or (assq field slots)
889 nil) ; Make coverage-tester happy. 810 ;; (error "Unknown field %S" field)))
811 ;; (idx (- (length slots)
812 ;; (length (memq slot slots)))))
813 ;; `(ses-aset-with-undo cell ,idx val))
814 (let ((getter (intern-soft (format "ses-cell--%s" field))))
815 `(ses-setter-with-undo
816 (eval-when-compile
817 (cons #',getter
818 (lambda (newval cell)
819 (setf (,getter cell) newval))))
820 val cell))))))
821 (if change
822 (add-to-list 'ses--deferred-write (cons row col))))
823 nil)) ; Make coverage-tester happy.
890 824
891(defun ses-cell-set-formula (row col formula) 825(defun ses-cell-set-formula (row col formula)
892 "Store a new formula for (ROW . COL) and enqueue the cell for 826 "Store a new formula for (ROW . COL) and enqueue the cell for
@@ -901,7 +835,7 @@ means Emacs will crash if FORMULA contains a circular list."
901 (newref (ses-formula-references formula)) 835 (newref (ses-formula-references formula))
902 (inhibit-quit t) 836 (inhibit-quit t)
903 x xrow xcol) 837 x xrow xcol)
904 (add-to-list 'ses--deferred-recalc sym) 838 (cl-pushnew sym ses--deferred-recalc)
905 ;;Delete old references from this cell. Skip the ones that are also 839 ;;Delete old references from this cell. Skip the ones that are also
906 ;;in the new list. 840 ;;in the new list.
907 (dolist (ref oldref) 841 (dolist (ref oldref)
@@ -932,11 +866,11 @@ means Emacs will crash if FORMULA contains a circular list."
932 (dotimes (col ses--numcols) 866 (dotimes (col ses--numcols)
933 (let ((references (ses-cell-property-pop :ses-repair-reference 867 (let ((references (ses-cell-property-pop :ses-repair-reference
934 row col))) 868 row col)))
935 (when references 869 (when references
936 (push (list 870 (push (list (ses-cell-symbol row col)
937 (ses-cell-symbol row col) 871 :corrupt-property
938 :corrupt-property 872 references)
939 references) errors))))) 873 errors)))))
940 874
941 ;; Step 2, build new. 875 ;; Step 2, build new.
942 (dotimes (row ses--numrows) 876 (dotimes (row ses--numrows)
@@ -946,21 +880,17 @@ means Emacs will crash if FORMULA contains a circular list."
946 (formula (ses-cell-formula cell)) 880 (formula (ses-cell-formula cell))
947 (new-ref (ses-formula-references formula))) 881 (new-ref (ses-formula-references formula)))
948 (dolist (ref new-ref) 882 (dolist (ref new-ref)
949 (let* ((rowcol (ses-sym-rowcol ref)) 883 (let ((rowcol (ses-sym-rowcol ref)))
950 (h (ses-cell-property-get-handle :ses-repair-reference 884 (cl-pushnew sym (ses-cell-property :ses-repair-reference
951 (car rowcol) (cdr rowcol)))) 885 (car rowcol)
952 (unless (memq ref (ses-cell-property-handle-car h)) 886 (cdr rowcol))))))))
953 (ses-cell-property-handle-setcar
954 h
955 (cons sym
956 (ses-cell-property-handle-car h)))))))))
957 887
958 ;; Step 3, overwrite with check. 888 ;; Step 3, overwrite with check.
959 (dotimes (row ses--numrows) 889 (dotimes (row ses--numrows)
960 (dotimes (col ses--numcols) 890 (dotimes (col ses--numcols)
961 (let* ((cell (ses-get-cell row col)) 891 (let* ((cell (ses-get-cell row col))
962 (irrelevant (ses-cell-references cell)) 892 (irrelevant (ses-cell-references cell))
963 (new-ref (ses-cell-property-pop :ses-repair-reference cell)) 893 (new-ref (ses-cell-property-pop :ses-repair-reference cell))
964 missing) 894 missing)
965 (dolist (ref new-ref) 895 (dolist (ref new-ref)
966 (if (memq ref irrelevant) 896 (if (memq ref irrelevant)
@@ -973,7 +903,7 @@ means Emacs will crash if FORMULA contains a circular list."
973 ,@(and irrelevant (list :irrelevant irrelevant))) 903 ,@(and irrelevant (list :irrelevant irrelevant)))
974 errors))))) 904 errors)))))
975 (if errors 905 (if errors
976 (warn "---------------------------------------------------------------- 906 (warn "----------------------------------------------------------------
977Some references were corrupted. 907Some references were corrupted.
978 908
979The following is a list where each element ELT is such 909The following is a list where each element ELT is such
@@ -1004,12 +934,7 @@ the old and FORCE is nil."
1004 (let ((oldval (ses-cell-value cell)) 934 (let ((oldval (ses-cell-value cell))
1005 (formula (ses-cell-formula cell)) 935 (formula (ses-cell-formula cell))
1006 newval 936 newval
1007 this-cell-Dijkstra-attempt-h 937 this-cell-Dijkstra-attempt+1)
1008 this-cell-Dijkstra-attempt
1009 this-cell-Dijkstra-attempt+1
1010 ref-cell-Dijkstra-attempt-h
1011 ref-cell-Dijkstra-attempt
1012 ref-rowcol)
1013 (when (eq (car-safe formula) 'ses-safe-formula) 938 (when (eq (car-safe formula) 'ses-safe-formula)
1014 (setq formula (ses-safe-formula (cadr formula))) 939 (setq formula (ses-safe-formula (cadr formula)))
1015 (ses-set-cell row col 'formula formula)) 940 (ses-set-cell row col 'formula formula))
@@ -1025,46 +950,42 @@ the old and FORCE is nil."
1025 (setq newval '*skip*)) 950 (setq newval '*skip*))
1026 (catch 'cycle 951 (catch 'cycle
1027 (when (or force (not (eq newval oldval))) 952 (when (or force (not (eq newval oldval)))
1028 (add-to-list 'ses--deferred-write (cons row col)) ; In case force=t. 953 (cl-pushnew (cons row col) ses--deferred-write :test #'equal) ; In case force=t.
1029 (setq this-cell-Dijkstra-attempt-h 954 (ses--letref (pget pset)
1030 (ses-cell-property-get-handle :ses-Dijkstra-attempt cell); 955 (ses-cell-property :ses-Dijkstra-attempt cell)
1031 this-cell-Dijkstra-attempt 956 (let ((this-cell-Dijkstra-attempt (pget)))
1032 (ses-cell-property-handle-car this-cell-Dijkstra-attempt-h)) 957 (if (null this-cell-Dijkstra-attempt)
1033 (if (null this-cell-Dijkstra-attempt) 958 (pset
1034 (ses-cell-property-handle-setcar 959 (setq this-cell-Dijkstra-attempt
1035 this-cell-Dijkstra-attempt-h 960 (cons ses--Dijkstra-attempt-nb 0)))
1036 (setq this-cell-Dijkstra-attempt 961 (unless (= ses--Dijkstra-attempt-nb
1037 (cons ses--Dijkstra-attempt-nb 0))) 962 (car this-cell-Dijkstra-attempt))
1038 (unless (= ses--Dijkstra-attempt-nb 963 (setcar this-cell-Dijkstra-attempt ses--Dijkstra-attempt-nb)
1039 (car this-cell-Dijkstra-attempt)) 964 (setcdr this-cell-Dijkstra-attempt 0)))
1040 (setcar this-cell-Dijkstra-attempt ses--Dijkstra-attempt-nb) 965 (setq this-cell-Dijkstra-attempt+1
1041 (setcdr this-cell-Dijkstra-attempt 0))) 966 (1+ (cdr this-cell-Dijkstra-attempt)))))
1042 (setq this-cell-Dijkstra-attempt+1
1043 (1+ (cdr this-cell-Dijkstra-attempt)))
1044 (ses-set-cell row col 'value newval) 967 (ses-set-cell row col 'value newval)
1045 (dolist (ref (ses-cell-references cell)) 968 (dolist (ref (ses-cell-references cell))
1046 (add-to-list 'ses--deferred-recalc ref) 969 (cl-pushnew ref ses--deferred-recalc)
1047 (setq ref-rowcol (ses-sym-rowcol ref) 970 (ses--letref (pget pset)
1048 ref-cell-Dijkstra-attempt-h 971 (let ((ref-rowcol (ses-sym-rowcol ref)))
1049 (ses-cell-property-get-handle 972 (ses-cell-property
1050 :ses-Dijkstra-attempt 973 :ses-Dijkstra-attempt
1051 (car ref-rowcol) (cdr ref-rowcol)) 974 (car ref-rowcol) (cdr ref-rowcol)))
1052 ref-cell-Dijkstra-attempt 975 (let ((ref-cell-Dijkstra-attempt (pget)))
1053 (ses-cell-property-handle-car ref-cell-Dijkstra-attempt-h)) 976
1054 977 (if (null ref-cell-Dijkstra-attempt)
1055 (if (null ref-cell-Dijkstra-attempt) 978 (pset
1056 (ses-cell-property-handle-setcar 979 (setq ref-cell-Dijkstra-attempt
1057 ref-cell-Dijkstra-attempt-h 980 (cons ses--Dijkstra-attempt-nb
1058 (setq ref-cell-Dijkstra-attempt 981 this-cell-Dijkstra-attempt+1)))
1059 (cons ses--Dijkstra-attempt-nb 982 (if (= (car ref-cell-Dijkstra-attempt) ses--Dijkstra-attempt-nb)
1060 this-cell-Dijkstra-attempt+1))) 983 (setcdr ref-cell-Dijkstra-attempt
1061 (if (= (car ref-cell-Dijkstra-attempt) ses--Dijkstra-attempt-nb) 984 (max (cdr ref-cell-Dijkstra-attempt)
1062 (setcdr ref-cell-Dijkstra-attempt 985 this-cell-Dijkstra-attempt+1))
1063 (max (cdr ref-cell-Dijkstra-attempt) 986 (setcar ref-cell-Dijkstra-attempt ses--Dijkstra-attempt-nb)
1064 this-cell-Dijkstra-attempt+1)) 987 (setcdr ref-cell-Dijkstra-attempt
1065 (setcar ref-cell-Dijkstra-attempt ses--Dijkstra-attempt-nb) 988 this-cell-Dijkstra-attempt+1)))))
1066 (setcdr ref-cell-Dijkstra-attempt
1067 this-cell-Dijkstra-attempt+1)))
1068 989
1069 (when (> this-cell-Dijkstra-attempt+1 ses--Dijkstra-weight-bound) 990 (when (> this-cell-Dijkstra-attempt+1 ses--Dijkstra-weight-bound)
1070 ;; Update print of this cell. 991 ;; Update print of this cell.
@@ -1123,7 +1044,7 @@ if the cell's value is unchanged and FORCE is nil."
1123 (when (or (memq ref curlist) 1044 (when (or (memq ref curlist)
1124 (memq ref ses--deferred-recalc)) 1045 (memq ref ses--deferred-recalc))
1125 ;; This cell refers to another that isn't done yet 1046 ;; This cell refers to another that isn't done yet
1126 (add-to-list 'ses--deferred-recalc this-sym) 1047 (cl-pushnew this-sym ses--deferred-recalc :test #'equal)
1127 (throw 'ref t))))) 1048 (throw 'ref t)))))
1128 ;; ses-update-cells is called from post-command-hook, so 1049 ;; ses-update-cells is called from post-command-hook, so
1129 ;; inhibit-quit is implicitly bound to t. 1050 ;; inhibit-quit is implicitly bound to t.
@@ -1132,7 +1053,7 @@ if the cell's value is unchanged and FORCE is nil."
1132 (error "Quit")) 1053 (error "Quit"))
1133 (ses-calculate-cell (car this-rowcol) (cdr this-rowcol) force))) 1054 (ses-calculate-cell (car this-rowcol) (cdr this-rowcol) force)))
1134 (dolist (ref ses--deferred-recalc) 1055 (dolist (ref ses--deferred-recalc)
1135 (add-to-list 'nextlist ref))) 1056 (cl-pushnew ref nextlist :test #'equal)))
1136 (when ses--deferred-recalc 1057 (when ses--deferred-recalc
1137 ;; Just couldn't finish these. 1058 ;; Just couldn't finish these.
1138 (dolist (x ses--deferred-recalc) 1059 (dolist (x ses--deferred-recalc)
@@ -1251,7 +1172,8 @@ preceding cell has spilled over."
1251 ((< len width) 1172 ((< len width)
1252 ;; Fill field to length with spaces. 1173 ;; Fill field to length with spaces.
1253 (setq len (make-string (- width len) ?\s) 1174 (setq len (make-string (- width len) ?\s)
1254 text (if (eq ses-call-printer-return t) 1175 text (if (or (stringp value)
1176 (eq ses-call-printer-return t))
1255 (concat text len) 1177 (concat text len)
1256 (concat len text)))) 1178 (concat len text))))
1257 ((> len width) 1179 ((> len width)
@@ -1352,7 +1274,7 @@ printer signaled one (and \"%s\" is used as the default printer), else nil."
1352 (or (and (symbolp printer) 1274 (or (and (symbolp printer)
1353 (let ((locprn (gethash printer ses--local-printer-hashmap))) 1275 (let ((locprn (gethash printer ses--local-printer-hashmap)))
1354 (and locprn 1276 (and locprn
1355 (ses-locprn-get-compiled locprn)))) 1277 (ses--locprn-compiled locprn))))
1356 printer) 1278 printer)
1357 (or value ""))) 1279 (or value "")))
1358 (if (stringp value) 1280 (if (stringp value)
@@ -1440,7 +1362,8 @@ undoable. Return nil when there was no change, and non nil otherwise."
1440 (ses-widen) 1362 (ses-widen)
1441 (goto-char ses--params-marker) 1363 (goto-char ses--params-marker)
1442 (forward-line (plist-get ses-paramlines-plist 'ses--numlocprn )) 1364 (forward-line (plist-get ses-paramlines-plist 'ses--numlocprn ))
1443 (insert (format (plist-get ses-paramfmt-plist 'ses--numlocprn) ses--numlocprn) 1365 (insert (format (plist-get ses-paramfmt-plist 'ses--numlocprn)
1366 ses--numlocprn)
1444 ?\n) 1367 ?\n)
1445 t) ))) 1368 t) )))
1446 1369
@@ -1492,24 +1415,17 @@ Newlines in the data are escaped."
1492 (setq formula (cadr formula))) 1415 (setq formula (cadr formula)))
1493 (if (eq (car-safe printer) 'ses-safe-printer) 1416 (if (eq (car-safe printer) 'ses-safe-printer)
1494 (setq printer (cadr printer))) 1417 (setq printer (cadr printer)))
1495 ;; This is noticeably faster than (format "%S %S %S %S %S") 1418 (setq text (prin1-to-string
1496 (setq text (concat "(ses-cell " 1419 ;; We could shorten it to (ses-cell SYM VAL) when
1497 (symbol-name sym) 1420 ;; the other parameters are nil, but in practice most
1498 " " 1421 ;; cells have non-nil `references', so it's
1499 (prin1-to-string (symbol-value sym)) 1422 ;; rather pointless.
1500 " " 1423 `(ses-cell ,sym
1501 (prin1-to-string formula) 1424 ,(symbol-value sym)
1502 " " 1425 ,(unless (equal formula (symbol-value sym))
1503 (prin1-to-string printer) 1426 formula)
1504 " " 1427 ,printer
1505 (if (atom (ses-cell-references cell)) 1428 ,(ses-cell-references cell))))
1506 "nil"
1507 (concat "("
1508 (mapconcat 'symbol-name
1509 (ses-cell-references cell)
1510 " ")
1511 ")"))
1512 ")"))
1513 (ses-goto-data row col) 1429 (ses-goto-data row col)
1514 (delete-region (point) (line-end-position)) 1430 (delete-region (point) (line-end-position))
1515 (insert text))) 1431 (insert text)))
@@ -1526,8 +1442,8 @@ refers to. For recursive calls, RESULT-SO-FAR is the list being
1526constructed, or t to get a wrong-type-argument error when the 1442constructed, or t to get a wrong-type-argument error when the
1527first reference is found." 1443first reference is found."
1528 (if (ses-sym-rowcol formula) 1444 (if (ses-sym-rowcol formula)
1529 ;;Entire formula is one symbol 1445 ;; Entire formula is one symbol.
1530 (add-to-list 'result-so-far formula) 1446 (cl-pushnew formula result-so-far :test #'equal)
1531 (if (consp formula) 1447 (if (consp formula)
1532 (cond 1448 (cond
1533 ((eq (car formula) 'ses-range) 1449 ((eq (car formula) 'ses-range)
@@ -1535,7 +1451,7 @@ first reference is found."
1535 (cdr (funcall 'macroexpand 1451 (cdr (funcall 'macroexpand
1536 (list 'ses-range (nth 1 formula) 1452 (list 'ses-range (nth 1 formula)
1537 (nth 2 formula))))) 1453 (nth 2 formula)))))
1538 (add-to-list 'result-so-far cur))) 1454 (cl-pushnew cur result-so-far :test #'equal)))
1539 ((null (eq (car formula) 'quote)) 1455 ((null (eq (car formula) 'quote))
1540 ;;Recursive call for subformulas 1456 ;;Recursive call for subformulas
1541 (dolist (cur formula) 1457 (dolist (cur formula)
@@ -1704,8 +1620,8 @@ to each symbol."
1704 ;; This cell referred to a cell that's been deleted or is no 1620 ;; This cell referred to a cell that's been deleted or is no
1705 ;; longer part of the range. We can't fix that now because 1621 ;; longer part of the range. We can't fix that now because
1706 ;; reference lists cells have been partially updated. 1622 ;; reference lists cells have been partially updated.
1707 (add-to-list 'ses--deferred-recalc 1623 (cl-pushnew (ses-create-cell-symbol row col)
1708 (ses-create-cell-symbol row col))) 1624 ses--deferred-recalc :test #'equal))
1709 (setq newval (ses-relocate-formula (ses-cell-references mycell) 1625 (setq newval (ses-relocate-formula (ses-cell-references mycell)
1710 minrow mincol rowincr colincr)) 1626 minrow mincol rowincr colincr))
1711 (ses-set-cell row col 'references newval) 1627 (ses-set-cell row col 'references newval)
@@ -1795,36 +1711,30 @@ to each symbol."
1795 (insert-and-inherit "X") 1711 (insert-and-inherit "X")
1796 (delete-region (1- (point)) (point)))) 1712 (delete-region (1- (point)) (point))))
1797 1713
1798(defun ses-set-with-undo (sym newval) 1714(defun ses-setter-with-undo (accessors newval &rest args)
1799 "Like set, but undoable. Result is t if value has changed." 1715 "Set a field/variable and record it so it can be undone.
1800 ;; We try to avoid adding redundant entries to the undo list, but this is 1716Result is non-nil if field/variable has changed."
1801 ;; unavoidable for strings because equal ignores text properties and there's 1717 (let ((oldval (apply (car accessors) args)))
1802 ;; no easy way to get the whole property list to see if it's different! 1718 (unless (equal-including-properties oldval newval)
1803 (unless (and (boundp sym) 1719 (push `(apply ses-setter-with-undo ,accessors ,oldval ,@args)
1804 (equal (symbol-value sym) newval) 1720 buffer-undo-list)
1805 (not (stringp newval))) 1721 (apply (cdr accessors) newval args)
1806 (push (if (boundp sym) 1722 t)))
1807 `(apply ses-set-with-undo ,sym ,(symbol-value sym))
1808 `(apply ses-unset-with-undo ,sym))
1809 buffer-undo-list)
1810 (set sym newval)
1811 t))
1812
1813(defun ses-unset-with-undo (sym)
1814 "Set SYM to be unbound. This is undoable."
1815 (when (1value (boundp sym)) ; Always bound, except after a programming error.
1816 (push `(apply ses-set-with-undo ,sym ,(symbol-value sym)) buffer-undo-list)
1817 (makunbound sym)))
1818 1723
1819(defun ses-aset-with-undo (array idx newval) 1724(defun ses-aset-with-undo (array idx newval)
1820 "Like `aset', but undoable. 1725 (ses-setter-with-undo (eval-when-compile
1821Result is t if element has changed." 1726 (cons #'aref
1822 (unless (equal (aref array idx) newval) 1727 (lambda (newval array idx) (aset array idx newval))))
1823 (push `(apply ses-aset-with-undo ,array ,idx 1728 newval array idx))
1824 ,(aref array idx)) buffer-undo-list)
1825 (aset array idx newval)
1826 t))
1827 1729
1730(defun ses-set-with-undo (sym newval)
1731 (ses-setter-with-undo
1732 (eval-when-compile
1733 (cons (lambda (sym) (if (boundp sym) (symbol-value sym) :ses--unbound))
1734 (lambda (newval sym) (if (eq newval :ses--unbound)
1735 (makunbound sym)
1736 (set sym newval)))))
1737 newval sym))
1828 1738
1829;;---------------------------------------------------------------------------- 1739;;----------------------------------------------------------------------------
1830;; Startup for major mode 1740;; Startup for major mode
@@ -1890,11 +1800,11 @@ Does not execute cell formulas or print functions."
1890 (forward-line (* ses--numrows (1+ ses--numcols))) 1800 (forward-line (* ses--numrows (1+ ses--numcols)))
1891 (let ((numlocprn ses--numlocprn)) 1801 (let ((numlocprn ses--numlocprn))
1892 (setq ses--numlocprn 0) 1802 (setq ses--numlocprn 0)
1893 (dotimes (lp numlocprn) 1803 (dotimes (_ numlocprn)
1894 (let ((x (read (current-buffer)))) 1804 (let ((x (read (current-buffer))))
1895 (or (and (looking-at-p "\n") 1805 (or (and (looking-at-p "\n")
1896 (eq (car-safe x) 'ses-local-printer) 1806 (eq (car-safe x) 'ses-local-printer)
1897 (eval x)) 1807 (apply #'ses--local-printer (cdr x)))
1898 (error "local printer-def error")) 1808 (error "local printer-def error"))
1899 (setq ses--numlocprn (1+ ses--numlocprn)))))) 1809 (setq ses--numlocprn (1+ ses--numlocprn))))))
1900 ;; Load cell definitions. 1810 ;; Load cell definitions.
@@ -1906,7 +1816,7 @@ Does not execute cell formulas or print functions."
1906 (eq (car-safe x) 'ses-cell) 1816 (eq (car-safe x) 'ses-cell)
1907 (ses-create-cell-variable sym row col)) 1817 (ses-create-cell-variable sym row col))
1908 (error "Cell-def error")) 1818 (error "Cell-def error"))
1909 (eval x))) 1819 (apply #'ses--cell (cdr x))))
1910 (or (looking-at-p "\n\n") 1820 (or (looking-at-p "\n\n")
1911 (error "Missing blank line between rows"))) 1821 (error "Missing blank line between rows")))
1912 ;; Skip local printer function declaration --- that were already loaded. 1822 ;; Skip local printer function declaration --- that were already loaded.
@@ -2067,7 +1977,8 @@ formula:
2067 ;; calculation). 1977 ;; calculation).
2068 indent-tabs-mode nil) 1978 indent-tabs-mode nil)
2069 (1value (add-hook 'change-major-mode-hook 'ses-cleanup nil t)) 1979 (1value (add-hook 'change-major-mode-hook 'ses-cleanup nil t))
2070 (1value (add-hook 'before-revert-hook 'ses-cleanup nil t)) 1980 ;; This makes revert impossible if the buffer is read-only.
1981 ;; (1value (add-hook 'before-revert-hook 'ses-cleanup nil t))
2071 (setq header-line-format '(:eval (progn 1982 (setq header-line-format '(:eval (progn
2072 (when (/= (window-hscroll) 1983 (when (/= (window-hscroll)
2073 ses--header-hscroll) 1984 ses--header-hscroll)
@@ -2251,7 +2162,7 @@ print area if NONARROW is nil."
2251 (delete-region (point-min) (point)) 2162 (delete-region (point-min) (point))
2252 ;; Insert all blank lines before printing anything, so ses-print-cell can 2163 ;; Insert all blank lines before printing anything, so ses-print-cell can
2253 ;; find the data area when inserting or deleting *skip* values for cells. 2164 ;; find the data area when inserting or deleting *skip* values for cells.
2254 (dotimes (row ses--numrows) 2165 (dotimes (_ ses--numrows)
2255 (insert-and-inherit ses--blank-line)) 2166 (insert-and-inherit ses--blank-line))
2256 (dotimes-with-progress-reporter (row ses--numrows) "Reprinting..." 2167 (dotimes-with-progress-reporter (row ses--numrows) "Reprinting..."
2257 (if (eq (ses-cell-value row 0) '*skip*) 2168 (if (eq (ses-cell-value row 0) '*skip*)
@@ -2283,9 +2194,10 @@ to are recalculated first."
2283 (when 2194 (when
2284 (setq cur-rowcol (ses-sym-rowcol ses--curcell) 2195 (setq cur-rowcol (ses-sym-rowcol ses--curcell)
2285 sig (progn 2196 sig (progn
2286 (ses-cell-property-set :ses-Dijkstra-attempt 2197 (setf (ses-cell-property :ses-Dijkstra-attempt
2287 (cons ses--Dijkstra-attempt-nb 0) 2198 (car cur-rowcol)
2288 (car cur-rowcol) (cdr cur-rowcol) ) 2199 (cdr cur-rowcol))
2200 (cons ses--Dijkstra-attempt-nb 0))
2289 (ses-calculate-cell (car cur-rowcol) (cdr cur-rowcol) t))) 2201 (ses-calculate-cell (car cur-rowcol) (cdr cur-rowcol) t)))
2290 (nconc sig (list (ses-cell-symbol (car cur-rowcol) 2202 (nconc sig (list (ses-cell-symbol (car cur-rowcol)
2291 (cdr cur-rowcol))))) 2203 (cdr cur-rowcol)))))
@@ -2298,14 +2210,14 @@ to are recalculated first."
2298 ;; The t causes an error if the cell has references. If no 2210 ;; The t causes an error if the cell has references. If no
2299 ;; references, the t will be the result value. 2211 ;; references, the t will be the result value.
2300 (1value (ses-formula-references (ses-cell-formula row col) t)) 2212 (1value (ses-formula-references (ses-cell-formula row col) t))
2301 (ses-cell-property-set :ses-Dijkstra-attempt 2213 (setf (ses-cell-property :ses-Dijkstra-attempt row col)
2302 (cons ses--Dijkstra-attempt-nb 0) 2214 (cons ses--Dijkstra-attempt-nb 0))
2303 row col)
2304 (when (setq sig (ses-calculate-cell row col t)) 2215 (when (setq sig (ses-calculate-cell row col t))
2305 (nconc sig (list (ses-cell-symbol row col))))) 2216 (nconc sig (list (ses-cell-symbol row col)))))
2306 (wrong-type-argument 2217 (wrong-type-argument
2307 ;; The formula contains a reference. 2218 ;; The formula contains a reference.
2308 (add-to-list 'ses--deferred-recalc (ses-cell-symbol row col)))))) 2219 (cl-pushnew (ses-cell-symbol row col) ses--deferred-recalc
2220 :test #'equal)))))
2309 ;; Do the update now, so we can force recalculation. 2221 ;; Do the update now, so we can force recalculation.
2310 (let ((x ses--deferred-recalc)) 2222 (let ((x ses--deferred-recalc))
2311 (setq ses--deferred-recalc nil) 2223 (setq ses--deferred-recalc nil)
@@ -2380,7 +2292,7 @@ to are recalculated first."
2380 (insert ses-initial-file-trailer) 2292 (insert ses-initial-file-trailer)
2381 (goto-char (point-min))) 2293 (goto-char (point-min)))
2382 ;; Create a blank display area. 2294 ;; Create a blank display area.
2383 (dotimes (row ses--numrows) 2295 (dotimes (_ ses--numrows)
2384 (insert ses--blank-line)) 2296 (insert ses--blank-line))
2385 (insert ses-print-data-boundary) 2297 (insert ses-print-data-boundary)
2386 (backward-char (1- (length ses-print-data-boundary))) 2298 (backward-char (1- (length ses-print-data-boundary)))
@@ -2450,16 +2362,23 @@ cell formula was unsafe and user declined confirmation."
2450 (barf-if-buffer-read-only) 2362 (barf-if-buffer-read-only)
2451 (list (car rowcol) 2363 (list (car rowcol)
2452 (cdr rowcol) 2364 (cdr rowcol)
2453 (read-from-minibuffer 2365 (if (equal initial "\"")
2454 (format "Cell %s: " ses--curcell) 2366 (progn
2455 (cons (if (equal initial "\"") "\"\"" 2367 (if (not (stringp curval)) (setq curval nil))
2456 (if (equal initial "(") "()" initial)) 2) 2368 (read-string (if curval
2457 ses-mode-edit-map 2369 (format "String Cell %s (default %s): "
2458 t ; Convert to Lisp object. 2370 ses--curcell curval)
2459 'ses-read-cell-history 2371 (format "String Cell %s: " ses--curcell))
2460 (prin1-to-string (if (eq (car-safe curval) 'ses-safe-formula) 2372 nil 'ses-read-string-history curval))
2461 (cadr curval) 2373 (read-from-minibuffer
2462 curval)))))) 2374 (format "Cell %s: " ses--curcell)
2375 (cons (if (equal initial "(") "()" initial) 2)
2376 ses-mode-edit-map
2377 t ; Convert to Lisp object.
2378 'ses-read-cell-history
2379 (prin1-to-string (if (eq (car-safe curval) 'ses-safe-formula)
2380 (cadr curval)
2381 curval)))))))
2463 (when (ses-edit-cell row col newval) 2382 (when (ses-edit-cell row col newval)
2464 (ses-command-hook) ; Update cell widths before movement. 2383 (ses-command-hook) ; Update cell widths before movement.
2465 (dolist (x ses-after-entry-functions) 2384 (dolist (x ses-after-entry-functions)
@@ -2492,7 +2411,7 @@ With prefix, deletes several cells."
2492 (1value (ses-clear-cell-backward (- count))) 2411 (1value (ses-clear-cell-backward (- count)))
2493 (ses-check-curcell) 2412 (ses-check-curcell)
2494 (ses-begin-change) 2413 (ses-begin-change)
2495 (dotimes (x count) 2414 (dotimes (_ count)
2496 (ses-set-curcell) 2415 (ses-set-curcell)
2497 (let ((rowcol (ses-sym-rowcol ses--curcell))) 2416 (let ((rowcol (ses-sym-rowcol ses--curcell)))
2498 (or rowcol (signal 'end-of-buffer nil)) 2417 (or rowcol (signal 'end-of-buffer nil))
@@ -2507,7 +2426,7 @@ cells."
2507 (1value (ses-clear-cell-forward (- count))) 2426 (1value (ses-clear-cell-forward (- count)))
2508 (ses-check-curcell 'end) 2427 (ses-check-curcell 'end)
2509 (ses-begin-change) 2428 (ses-begin-change)
2510 (dotimes (x count) 2429 (dotimes (_ count)
2511 (backward-char 1) ; Will signal 'beginning-of-buffer if appropriate. 2430 (backward-char 1) ; Will signal 'beginning-of-buffer if appropriate.
2512 (ses-set-curcell) 2431 (ses-set-curcell)
2513 (let ((rowcol (ses-sym-rowcol ses--curcell))) 2432 (let ((rowcol (ses-sym-rowcol ses--curcell)))
@@ -2526,7 +2445,7 @@ canceled."
2526 (barf-if-buffer-read-only) 2445 (barf-if-buffer-read-only)
2527 (if (eq default t) 2446 (if (eq default t)
2528 (setq default "") 2447 (setq default "")
2529 (setq prompt (format "%s [currently %S]: " 2448 (setq prompt (format "%s (default %S): "
2530 (substring prompt 0 -2) 2449 (substring prompt 0 -2)
2531 default))) 2450 default)))
2532 (let ((new (read-from-minibuffer prompt 2451 (let ((new (read-from-minibuffer prompt
@@ -2557,21 +2476,20 @@ one argument, or a symbol that names a function of one argument. In the
2557latter two cases, the function's result should be either a string (will be 2476latter two cases, the function's result should be either a string (will be
2558right-justified) or a list of one string (will be left-justified)." 2477right-justified) or a list of one string (will be left-justified)."
2559 (interactive 2478 (interactive
2560 (let ((default t) 2479 (let ((default t))
2561 x)
2562 (ses-check-curcell 'range) 2480 (ses-check-curcell 'range)
2563 ;;Default is none if not all cells in range have same printer 2481 ;;Default is none if not all cells in range have same printer
2564 (catch 'ses-read-cell-printer 2482 (catch 'ses-read-cell-printer
2565 (ses-dorange ses--curcell 2483 (ses-dorange ses--curcell
2566 (setq x (ses-cell-printer row col)) 2484 (let ((x (ses-cell-printer row col)))
2567 (if (eq (car-safe x) 'ses-safe-printer) 2485 (if (eq (car-safe x) 'ses-safe-printer)
2568 (setq x (cadr x))) 2486 (setq x (cadr x)))
2569 (if (eq default t) 2487 (if (eq default t)
2570 (setq default x) 2488 (setq default x)
2571 (unless (equal default x) 2489 (unless (equal default x)
2572 ;;Range contains differing printer functions 2490 ;;Range contains differing printer functions
2573 (setq default t) 2491 (setq default t)
2574 (throw 'ses-read-cell-printer t))))) 2492 (throw 'ses-read-cell-printer t))))))
2575 (list (ses-read-printer (format "Cell %S printer: " ses--curcell) 2493 (list (ses-read-printer (format "Cell %S printer: " ses--curcell)
2576 default)))) 2494 default))))
2577 (unless (eq newval t) 2495 (unless (eq newval t)
@@ -2850,7 +2768,7 @@ inserts a new row if at bottom of print area. Repeat COUNT times."
2850 (list col 2768 (list col
2851 (if current-prefix-arg 2769 (if current-prefix-arg
2852 (prefix-numeric-value current-prefix-arg) 2770 (prefix-numeric-value current-prefix-arg)
2853 (read-from-minibuffer (format "Column %s width [currently %d]: " 2771 (read-from-minibuffer (format "Column %s width (default %d): "
2854 (ses-column-letter col) 2772 (ses-column-letter col)
2855 (ses-col-width col)) 2773 (ses-col-width col))
2856 nil ; No initial contents. 2774 nil ; No initial contents.
@@ -3089,9 +3007,9 @@ cons of ROW and COL). Treat plain symbols as strings unless ARG is a list."
3089 ;; Invalid sexp --- leave it as a string. 3007 ;; Invalid sexp --- leave it as a string.
3090 (setq val (substring text from to))) 3008 (setq val (substring text from to)))
3091 ((and (car val) (symbolp (car val))) 3009 ((and (car val) (symbolp (car val)))
3092 (if (consp arg) 3010 (setq val (if (consp arg)
3093 (setq val (list 'quote (car val))) ; Keep symbol. 3011 (list 'quote (car val)) ; Keep symbol.
3094 (setq val (substring text from to)))) ; Treat symbol as text. 3012 (substring text from to)))) ; Treat symbol as text.
3095 (t 3013 (t
3096 (setq val (car val)))) 3014 (setq val (car val))))
3097 (let ((row (car rowcol)) 3015 (let ((row (car rowcol))
@@ -3437,29 +3355,31 @@ highlighted range in the spreadsheet."
3437 (if (equal new-rowcol rowcol) 3355 (if (equal new-rowcol rowcol)
3438 (put new-name 'ses-cell rowcol) 3356 (put new-name 'ses-cell rowcol)
3439 (error "Not a valid name for this cell location")) 3357 (error "Not a valid name for this cell location"))
3440 (setq ses--named-cell-hashmap (or ses--named-cell-hashmap (make-hash-table :test 'eq))) 3358 (setq ses--named-cell-hashmap
3359 (or ses--named-cell-hashmap (make-hash-table :test 'eq)))
3441 (put new-name 'ses-cell :ses-named) 3360 (put new-name 'ses-cell :ses-named)
3442 (puthash new-name rowcol ses--named-cell-hashmap)) 3361 (puthash new-name rowcol ses--named-cell-hashmap))
3443 (push `(ses-rename-cell ,old-name ,cell) buffer-undo-list) 3362 (push `(ses-rename-cell ,old-name ,cell) buffer-undo-list)
3444 ;; replace name by new name in formula of cells refering to renamed cell 3363 ;; Replace name by new name in formula of cells refering to renamed cell.
3445 (dolist (ref (ses-cell-references cell)) 3364 (dolist (ref (ses-cell-references cell))
3446 (let* ((x (ses-sym-rowcol ref)) 3365 (let* ((x (ses-sym-rowcol ref))
3447 (xcell (ses-get-cell (car x) (cdr x)))) 3366 (xcell (ses-get-cell (car x) (cdr x))))
3448 (ses-cell-formula-aset xcell 3367 (setf (ses-cell-formula xcell)
3449 (ses-replace-name-in-formula 3368 (ses-replace-name-in-formula
3450 (ses-cell-formula xcell) 3369 (ses-cell-formula xcell)
3451 sym 3370 sym
3452 new-name)))) 3371 new-name))))
3453 ;; replace name by new name in reference list of cells to which renamed cell refers to 3372 ;; Replace name by new name in reference list of cells to which renamed
3373 ;; cell refers to.
3454 (dolist (ref (ses-formula-references (ses-cell-formula cell))) 3374 (dolist (ref (ses-formula-references (ses-cell-formula cell)))
3455 (let* ((x (ses-sym-rowcol ref)) 3375 (let* ((x (ses-sym-rowcol ref))
3456 (xcell (ses-get-cell (car x) (cdr x)))) 3376 (xcell (ses-get-cell (car x) (cdr x))))
3457 (ses-cell-references-aset xcell 3377 (setf (ses-cell-references xcell)
3458 (cons new-name (delq sym 3378 (cons new-name (delq sym
3459 (ses-cell-references xcell)))))) 3379 (ses-cell-references xcell))))))
3460 (push new-name ses--renamed-cell-symb-list) 3380 (push new-name ses--renamed-cell-symb-list)
3461 (set new-name (symbol-value sym)) 3381 (set new-name (symbol-value sym))
3462 (aset cell 0 new-name) 3382 (setf (ses-cell--symbol cell) new-name)
3463 (makunbound sym) 3383 (makunbound sym)
3464 (and curcell (setq ses--curcell new-name)) 3384 (and curcell (setq ses--curcell new-name))
3465 (let* ((pos (point)) 3385 (let* ((pos (point))
@@ -3477,8 +3397,9 @@ highlighted range in the spreadsheet."
3477 (force-mode-line-update))) 3397 (force-mode-line-update)))
3478 3398
3479(defun ses-refresh-local-printer (name compiled-value) 3399(defun ses-refresh-local-printer (name compiled-value)
3480 "Refresh printout of spreadsheet for all cells with printer 3400 "Refresh printout for all cells which use printer NAME.
3481 defined to local printer named NAME using the value COMPILED-VALUE for this printer" 3401NAME should be the name of a locally defined printer.
3402Uses the value COMPILED-VALUE for this printer."
3482 (message "Refreshing cells using printer %S" name) 3403 (message "Refreshing cells using printer %S" name)
3483 (let (new-print) 3404 (let (new-print)
3484 (dotimes (row ses--numrows) 3405 (dotimes (row ses--numrows)
@@ -3490,55 +3411,58 @@ highlighted range in the spreadsheet."
3490 (ses-begin-change)) 3411 (ses-begin-change))
3491 (ses-print-cell row col))))))) 3412 (ses-print-cell row col)))))))
3492 3413
3493(defun ses-define-local-printer (printer-name) 3414(defun ses-define-local-printer (name)
3494 "Define a local printer with name PRINTER-NAME." 3415 "Define a local printer with name NAME."
3495 (interactive "*SEnter printer name: ") 3416 (interactive "*SEnter printer name: ")
3496 (let* ((cur-printer (gethash printer-name ses--local-printer-hashmap)) 3417 (let* ((cur-printer (gethash name ses--local-printer-hashmap))
3497 (default (and (vectorp cur-printer) (ses-locprn-get-def cur-printer))) 3418 (default (and (vectorp cur-printer) (ses--locprn-def cur-printer)))
3498 printer-def-text
3499 create-printer 3419 create-printer
3500 (new-printer (ses-read-printer (format "Enter definition of printer %S: " printer-name) default))) 3420 (new-def
3421 (ses-read-printer (format "Enter definition of printer %S: " name)
3422 default)))
3501 (cond 3423 (cond
3502 ;; cancelled operation => do nothing 3424 ;; cancelled operation => do nothing
3503 ((eq new-printer t)) 3425 ((eq new-def t))
3504 ;; no change => do nothing 3426 ;; no change => do nothing
3505 ((and (vectorp cur-printer) (equal new-printer default))) 3427 ((and (vectorp cur-printer) (equal new-def default)))
3506 ;; re-defined printer 3428 ;; re-defined printer
3507 ((vectorp cur-printer) 3429 ((vectorp cur-printer)
3508 (setq create-printer 0) 3430 (setq create-printer 0)
3509 (ses-locprn-def-aset cur-printer new-printer) 3431 (setf (ses--locprn-def cur-printer) new-def)
3510 (ses-refresh-local-printer 3432 (ses-refresh-local-printer
3511 printer-name 3433 name
3512 (ses-locprn-compiled-aset cur-printer (ses-local-printer-compile new-printer)))) 3434 (setf (ses--locprn-compiled cur-printer)
3435 (ses-local-printer-compile new-def))))
3513 ;; new definition 3436 ;; new definition
3514 (t 3437 (t
3515 (setq create-printer 1) 3438 (setq create-printer 1)
3516 (puthash printer-name 3439 (puthash name
3517 (setq cur-printer 3440 (setq cur-printer
3518 (ses-make-local-printer-info new-printer)) 3441 (ses-make-local-printer-info new-def))
3519 ses--local-printer-hashmap))) 3442 ses--local-printer-hashmap)))
3520 (when create-printer 3443 (when create-printer
3521 (setq printer-def-text 3444 (let ((printer-def-text
3522 (concat 3445 (concat
3523 "(ses-local-printer " 3446 "(ses-local-printer "
3524 (symbol-name printer-name) 3447 (symbol-name name)
3525 " " 3448 " "
3526 (prin1-to-string (ses-locprn-get-def cur-printer)) 3449 (prin1-to-string (ses--locprn-def cur-printer))
3527 ")")) 3450 ")")))
3528 (save-excursion 3451 (save-excursion
3529 (ses-goto-data ses--numrows 3452 (ses-goto-data ses--numrows
3530 (ses-locprn-get-number cur-printer)) 3453 (ses--locprn-number cur-printer))
3531 (let ((inhibit-read-only t)) 3454 (let ((inhibit-read-only t))
3532 ;; Special undo since it's outside the narrowed buffer. 3455 ;; Special undo since it's outside the narrowed buffer.
3533 (let (buffer-undo-list) 3456 (let (buffer-undo-list)
3534 (if (= create-printer 0) 3457 (if (= create-printer 0)
3535 (delete-region (point) (line-end-position)) 3458 (delete-region (point) (line-end-position))
3536 (insert ?\n) 3459 (insert ?\n)
3537 (backward-char)) 3460 (backward-char))
3538 (insert printer-def-text) 3461 (insert printer-def-text)
3539 (when (= create-printer 1) 3462 (when (= create-printer 1)
3540 (ses-file-format-extend-paramter-list 3) 3463 (ses-file-format-extend-paramter-list 3)
3541 (ses-set-parameter 'ses--numlocprn (+ ses--numlocprn create-printer))) ))))) ) 3464 (ses-set-parameter 'ses--numlocprn
3465 (+ ses--numlocprn create-printer))))))))))
3542 3466
3543 3467
3544;;---------------------------------------------------------------------------- 3468;;----------------------------------------------------------------------------