aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Belaïche2016-07-28 17:41:21 +0200
committerVincent Belaïche2016-07-28 17:41:21 +0200
commit41b28dea8587c13b0bc59c1ec70b65afab3aeeca (patch)
treedf194b2e078802337f57ad8af0e709bd994d84ee
parent3f4f21b406a56d504c03a9bab3ac0949d8774c17 (diff)
downloademacs-41b28dea8587c13b0bc59c1ec70b65afab3aeeca.tar.gz
emacs-41b28dea8587c13b0bc59c1ec70b65afab3aeeca.zip
Enable addition of local printers from a mode hook.
* doc/misc/ses.texi (Printer functions): Split the node into 5 sub-nodes + add some extra documentation. (Various kinds of printer functions): Make an itemisation to disintguish better the 3 types of printers, give an example of lambda printer definition. (Standard printer functions): Add documentation for ses-prin1 printer function. (Local printer functions): Add documentation for creating local printers programmatically from a hook. (Writing a lambda printer function): Add documentation about anti-stackoverflow precautions to take when you call the standard printer functions from inside a local printer. * lisp/ses.el (ses-standard-printer-functions): Add ses-prin1 among standard printer function, and update docstring accordingly. (ses-call-printer, ses-export-tab): Call `ses-prin1' instead of prin1-to-string. (ses-define-local-printer): Add definition to arguments so that a local printer can be defined programmatically from a mode hook. Make docstring more substantial. Use completing read for local printer name input. Plus some minor optimization. (ses-define-if-new-local-printer): New defsubst. (ses-center, ses-center-span, ses-dashfill) (ses-dashfill-span, ses-tildefill-span): Allow to pass printer as an optional argument to superseed column printer/default spreadsheet printer. (ses-prin1): New defun.
-rw-r--r--doc/misc/ses.texi189
-rw-r--r--lisp/ses.el125
2 files changed, 250 insertions, 64 deletions
diff --git a/doc/misc/ses.texi b/doc/misc/ses.texi
index 8b0bb82f174..1c5070b38a9 100644
--- a/doc/misc/ses.texi
+++ b/doc/misc/ses.texi
@@ -374,26 +374,62 @@ Undo previous action (@code{(undo)}).
374@cindex printer functions 374@cindex printer functions
375@cindex cell formatting 375@cindex cell formatting
376@cindex formatting cells 376@cindex formatting cells
377@findex ses-read-cell-printer
378@findex ses-read-column-printer
379@findex ses-read-default-printer
380@findex ses-define-local-printer
381@findex ses-center
382@findex ses-center-span
383@findex ses-dashfill
384@findex ses-dashfill-span
385@findex ses-tildefill-span
386
387 377
388Printer functions convert binary cell values into the print forms that 378Printer functions convert binary cell values into the print forms that
389Emacs will display on the screen. 379Emacs will display on the screen.
390 380
391A printer can be a format string, like @samp{"$%.2f"}. The result 381@menu
382* Various kinds of printer functions::
383* Configuring what printer function applies::
384* Standard printer functions::
385* Local printer functions::
386* Writing a lambda printer function::
387@end menu
388
389@node Various kinds of printer functions
390@subsection Various kinds of printer functions
391
392When configuring what printer function applies (@pxref{Configuring
393what printer function applies}), you can enter a printer function as
394one of the following:
395
396@itemize
397@item
398A format string, like @samp{"$%.2f"}. The result
392string is right-aligned within the print cell. To get left-alignment, 399string is right-aligned within the print cell. To get left-alignment,
393use parentheses: @samp{("$%.2f")}. A printer can also be a 400use parentheses: @samp{("$%.2f")}.
394one-argument function (a symbol or a lambda), whose result is a string 401@item
395(right-aligned) or list of one string (left-aligned). While typing in 402A printer can also be a one-argument function, the result of which is
396a lambda, you can use @kbd{M-@key{TAB}} to complete the names of symbols. 403a string (right-aligned) or list of one string (left-aligned). Such a
404function can be in turn configured as:
405@itemize
406@item
407A lambda expression, for instance:
408
409@lisp
410(lambda (x)
411 (cond
412 ((null x) "")
413 ((numberp x) (format "%.2f" x))
414 (t (ses-center-span x ?# 'ses-prin1))))
415@end lisp
416
417While typing in a lambda, you can use @kbd{M-@key{TAB}} to complete
418the names of symbols.
419@item
420A symbol referring to a standard printer function (@pxref{Standard
421printer functions}).
422@item
423A symbol referring to a local printer function (@pxref{Local printer
424functions}).
425@end itemize
426
427
428@end itemize
429
430
431@node Configuring what printer function applies
432@subsection Configuring what printer function applies
397 433
398Each cell has a printer. If @code{nil}, the column-printer for the cell's 434Each cell has a printer. If @code{nil}, the column-printer for the cell's
399column is used. If that is also @code{nil}, the default-printer for the 435column is used. If that is also @code{nil}, the default-printer for the
@@ -401,25 +437,35 @@ spreadsheet is used.
401 437
402@table @kbd 438@table @kbd
403@item p 439@item p
440@findex ses-read-cell-printer
404Enter a printer for current cell or range (@code{ses-read-cell-printer}). 441Enter a printer for current cell or range (@code{ses-read-cell-printer}).
405 442
406@item M-p 443@item M-p
444@findex ses-read-column-printer
407Enter a printer for the current column (@code{ses-read-column-printer}). 445Enter a printer for the current column (@code{ses-read-column-printer}).
408 446
409@item C-c C-p 447@item C-c C-p
448@findex ses-read-default-printer
410Enter the default printer for the spreadsheet 449Enter the default printer for the spreadsheet
411(@code{ses-read-default-printer}). 450(@code{ses-read-default-printer}).
412@end table 451@end table
413 452
414The @code{ses-read-@r{XXX}-printer} commands have their own minibuffer 453The @code{ses-read-@var{xxx}-printer} commands have their own
415history, which is preloaded with the set of all printers used in this 454minibuffer history, which is preloaded with the set of all printers
416spreadsheet, plus the standard printers. 455used in this spreadsheet, plus the standard printers (@pxref{Standard
456printer functions}) and the local printers (@pxref{Local printer
457functions}).
417 458
418The standard printers are suitable only for cells, not columns or 459@node Standard printer functions
419default, because they format the value using the column-printer (or 460@subsection Standard printer functions
420default-printer if @code{nil}) and then center the result:
421 461
422@table @code 462
463Except for @code{ses-prin1}, the other standard printers are suitable
464only for cells, not columns or default, because they format the value
465using the column-printer (or default-printer if @code{nil}) and then
466center the result:
467
468@ftable @code
423@item ses-center 469@item ses-center
424Just centering. 470Just centering.
425 471
@@ -434,8 +480,16 @@ Centering with dashes and spill-over.
434 480
435@item ses-tildefill-span 481@item ses-tildefill-span
436Centering with tildes (~) and spill-over. 482Centering with tildes (~) and spill-over.
437@end table
438 483
484@item ses-prin1
485This is the fallback printer, used when calling the configured printer
486throws some error.
487@end ftable
488
489@node Local printer functions
490@subsection Local printer functions
491
492@findex ses-define-local-printer
439You can define printer function local to a sheet with the command 493You can define printer function local to a sheet with the command
440@code{ses-define-local-printer}. For instance, define a printer 494@code{ses-define-local-printer}. For instance, define a printer
441@samp{foo} to @code{"%.2f"}, and then use symbol @samp{foo} as a 495@samp{foo} to @code{"%.2f"}, and then use symbol @samp{foo} as a
@@ -444,9 +498,50 @@ printer function. Then, if you call again
444@code{"%.3f"}, all the cells using printer @samp{foo} will be 498@code{"%.3f"}, all the cells using printer @samp{foo} will be
445reprinted accordingly. 499reprinted accordingly.
446 500
447When you define a printer function with a lambda expression taking one 501Sometimes there are local printers that you want to define or
448argument, please take care that the returned value is a string, or a 502re-define automatically every time you open a sheet. For instance
449list containing a string, even when the input argument has an 503imagine that you want to define/re-define automatically a local
504printer @code{euro} to display a number like an amount of euros, that
505is to say number @code{3.1} would be displayed as
506@code{3.10@dmn{}@euro{}}. To do so in any non read-only SES buffer,
507you can add some code like this to your @file{.emacs} init file:
508
509@lisp
510(defun my-ses-mode-hook ()
511 (unless buffer-read-only
512 (ses-define-local-printer
513 'euro
514 (lambda (x)
515 (cond
516 ((null x) "")
517 ((numberp x) (format "%.2f€" x))
518 (t (ses-center-span x ?# 'ses-prin1)))))))
519(add-hook 'ses-mode-hook 'my-ses-mode-hook)
520@end lisp
521
522If you replace command @code{ses-define-local-printer} by function
523@code{ses-define-if-new-local-printer}
524@findex ses-define-if-new-local-printer
525the definition will occur only if a local printer with the same name
526in not already defined.
527
528
529@node Writing a lambda printer function
530@subsection Writing a lambda printer function
531
532You can write a printer function with a lambda expression taking one
533argument in two cases:
534
535@itemize
536@item
537when you configure the printer function applying to a cell or column, or
538@item
539when you define a local printer function with command
540@code{ses-define-local-printer}.
541@end itemize
542
543When doing so, please take care that the returned value is a string,
544or a list containing a string, even when the input argument has an
450unexpected value. Here is an example: 545unexpected value. Here is an example:
451 546
452@example 547@example
@@ -454,10 +549,11 @@ unexpected value. Here is an example:
454 (cond 549 (cond
455 ((null val) "") 550 ((null val) "")
456 ((and (numberp val) (>= val 0)) (format "%.1f" val)) 551 ((and (numberp val) (>= val 0)) (format "%.1f" val))
457 (t (ses-center-span (format "%S" val) ?#)))) 552 (t (ses-center-span val ?# 'ses-prin1))))
458@end example 553@end example
459 554
460This example will: 555This example will:
556
461@itemize 557@itemize
462@item 558@item
463When the cell is empty (ie.@: when @code{val} is @code{nil}), print an 559When the cell is empty (ie.@: when @code{val} is @code{nil}), print an
@@ -467,12 +563,47 @@ When the cell value is a non negative number, format the the value in
467fixed-point notation with one decimal after point 563fixed-point notation with one decimal after point
468@item 564@item
469Otherwise, handle the value as erroneous by printing it as an 565Otherwise, handle the value as erroneous by printing it as an
470s-expression (using @code{prin1}), centered and surrounded by @code{#} 566s-expression (using @code{ses-prin1}), centered and surrounded by
471filling. 567@code{#} filling.
472@end itemize 568@end itemize
473 569
570Another precaution to take is to avoid stack-overflow (due to a
571printer function indefintely recursively re-calling itself). This can
572happen mistakenly when you use a local printer as a column printer,
573and this local printer implicitely call the current column printer, so
574will call itself recursively. Imagine for instance that you want to
575create some local printer @code{=fill} that would center the content
576of a cell and surround it by equal signs @code{=}, and you do it this
577way:
578
579@lisp
580(lambda (x)
581 (cond
582 ((null x) "")
583 (t (ses-center x 0 ?=))))
584@end lisp
474 585
586Because @code{=fill} uses standard printer @code{ses-center} without
587passing explicitely any printer to it, @code{ses-center} will call the
588current column printer if any or the spreadsheet default printer
589otherwise. So using @code{=fill} as a column printer will result in a
590stack overflow in this column. SES does not make any check for that,
591you just have to be careful. For instance re-write @code{=fill} like
592this:
593
594@lisp
595(lambda (x)
596 (cond
597 ((null x) "")
598 ((stringp x) (ses-center x 0 ?= " %s "))
599 (t (ses-center-span x ?# 'ses-prin1))))
600@end lisp
475 601
602The code above applies the @code{=} filling only to strings, it also
603surrounds the string by one space on each side before filling with
604@code{=} signs. So string @samp{Foo} will be displayed like @samp{@w{===
605Foo ===}} in an 11 character wide column. Anything else than empty cell
606or non string is displayed like errouneous by using @code{#} filling.
476 607
477@node Clearing cells 608@node Clearing cells
478@section Clearing cells 609@section Clearing cells
diff --git a/lisp/ses.el b/lisp/ses.el
index 305027c73bb..9d278b6d222 100644
--- a/lisp/ses.el
+++ b/lisp/ses.el
@@ -275,12 +275,15 @@ Each function is called with ARG=1."
275 "Display properties to create a raised box for cells in the header line.") 275 "Display properties to create a raised box for cells in the header line.")
276 276
277(defconst ses-standard-printer-functions 277(defconst ses-standard-printer-functions
278 '(ses-center ses-center-span ses-dashfill ses-dashfill-span 278 '(ses-center
279 ses-tildefill-span) 279 ses-center-span ses-dashfill ses-dashfill-span
280 "List of print functions to be included in initial history of printer 280 ses-tildefill-span
281functions. None of these standard-printer functions is suitable for use as a 281 ses-prin1)
282column printer or a global-default printer because they invoke the column or 282 "List of print functions to be included in initial history of
283default printer and then modify its output.") 283printer functions. None of these standard-printer functions,
284except function `ses-prin1', is suitable for use as a column
285printer or a global-default printer because they invoke the
286column or default printer and then modify its output.")
284 287
285 288
286;;---------------------------------------------------------------------------- 289;;----------------------------------------------------------------------------
@@ -1328,7 +1331,7 @@ printer signaled one (and \"%s\" is used as the default printer), else nil."
1328 (car value)))) 1331 (car value))))
1329 (error 1332 (error
1330 (setq ses-call-printer-return signal) 1333 (setq ses-call-printer-return signal)
1331 (prin1-to-string value t)))) 1334 (ses-prin1 value))))
1332 1335
1333(defun ses-adjust-print-width (col change) 1336(defun ses-adjust-print-width (col change)
1334 "Insert CHANGE spaces in front of column COL, or at end of line if 1337 "Insert CHANGE spaces in front of column COL, or at end of line if
@@ -3232,7 +3235,7 @@ is non-nil. Newlines and tabs in the export text are escaped."
3232 (when (eq (car-safe item) 'quote) 3235 (when (eq (car-safe item) 'quote)
3233 (push "'" result) 3236 (push "'" result)
3234 (setq item (cadr item))) 3237 (setq item (cadr item)))
3235 (setq item (prin1-to-string item t)) 3238 (setq item (ses-prin1 item))
3236 (setq item (replace-regexp-in-string "\t" "\\\\t" item)) 3239 (setq item (replace-regexp-in-string "\t" "\\\\t" item))
3237 (push item result) 3240 (push item result)
3238 (cond 3241 (cond
@@ -3531,34 +3534,67 @@ Uses the value COMPILED-VALUE for this printer."
3531 (ses-begin-change)) 3534 (ses-begin-change))
3532 (ses-print-cell row col))))))) 3535 (ses-print-cell row col)))))))
3533 3536
3534(defun ses-define-local-printer (name) 3537
3535 "Define a local printer with name NAME." 3538(defun ses-define-local-printer (name definition)
3536 (interactive "*SEnter printer name: ") 3539 "Define a local printer with name NAME and definition DEFINITION.
3540
3541NAME shall be a symbol. Use TAB to complete over existing local
3542printer names.
3543
3544DEFINITION shall be either a string formatter, e.g.:
3545
3546 \"%.2f\" or (\"%.2f\") for left alignment.
3547
3548or a lambda expression, e.g. for formatting in ISO format dates
3549created with a '(calcFunc-date YEAR MONTH DAY)' formula:
3550
3551 (lambda (x)
3552 (cond
3553 ((null val) \"\")
3554 ((eq (car-safe x) 'date)
3555 (let ((calc-format-date '(X YYYY \"-\" MM \"-\" DD)))
3556 (math-format-date x)))
3557 (t (ses-center-span val ?# 'ses-prin1))))
3558
3559If NAME is already used to name a local printer function, then
3560the current definition is proposed as default value, and the
3561function is redefined."
3562 (interactive
3563 (let (name def already-defined-names)
3564 (maphash (lambda (key val) (push (symbol-name key) already-defined-names))
3565 ses--local-printer-hashmap)
3566 (setq name (completing-read "Enter printer name: " already-defined-names))
3567 (when (string= name "")
3568 (error "Invalid printer name"))
3569 (setq name (intern name))
3570 (let* ((cur-printer (gethash name ses--local-printer-hashmap))
3571 (default (and cur-printer (ses--locprn-def cur-printer))))
3572 (setq def (ses-read-printer (format "Enter definition of printer %S: " name)
3573 default)))
3574 (list name def)))
3575
3537 (let* ((cur-printer (gethash name ses--local-printer-hashmap)) 3576 (let* ((cur-printer (gethash name ses--local-printer-hashmap))
3538 (default (and (vectorp cur-printer) (ses--locprn-def cur-printer))) 3577 (default (and cur-printer (ses--locprn-def cur-printer)))
3539 create-printer 3578 create-printer)
3540 (new-def
3541 (ses-read-printer (format "Enter definition of printer %S: " name)
3542 default)))
3543 (cond 3579 (cond
3544 ;; cancelled operation => do nothing 3580 ;; cancelled operation => do nothing
3545 ((eq new-def t)) 3581 ((eq definition t))
3546 ;; no change => do nothing 3582 ;; no change => do nothing
3547 ((and (vectorp cur-printer) (equal new-def default))) 3583 ((and cur-printer (equal definition default)))
3548 ;; re-defined printer 3584 ;; re-defined printer
3549 ((vectorp cur-printer) 3585 (cur-printer
3550 (setq create-printer 0) 3586 (setq create-printer 0)
3551 (setf (ses--locprn-def cur-printer) new-def) 3587 (setf (ses--locprn-def cur-printer) definition)
3552 (ses-refresh-local-printer 3588 (ses-refresh-local-printer
3553 name 3589 name
3554 (setf (ses--locprn-compiled cur-printer) 3590 (setf (ses--locprn-compiled cur-printer)
3555 (ses-local-printer-compile new-def)))) 3591 (ses-local-printer-compile definition))))
3556 ;; new definition 3592 ;; new definition
3557 (t 3593 (t
3558 (setq create-printer 1) 3594 (setq create-printer 1)
3559 (puthash name 3595 (puthash name
3560 (setq cur-printer 3596 (setq cur-printer
3561 (ses-make-local-printer-info new-def)) 3597 (ses-make-local-printer-info definition))
3562 ses--local-printer-hashmap))) 3598 ses--local-printer-hashmap)))
3563 (when create-printer 3599 (when create-printer
3564 (let ((printer-def-text 3600 (let ((printer-def-text
@@ -3582,8 +3618,18 @@ Uses the value COMPILED-VALUE for this printer."
3582 (when (= create-printer 1) 3618 (when (= create-printer 1)
3583 (ses-file-format-extend-parameter-list 3) 3619 (ses-file-format-extend-parameter-list 3)
3584 (ses-set-parameter 'ses--numlocprn 3620 (ses-set-parameter 'ses--numlocprn
3585 (+ ses--numlocprn create-printer)))))))))) 3621 (1+ ses--numlocprn))))))))))
3586 3622
3623(defsubst ses-define-if-new-local-printer (name def)
3624 "Same as function `ses-define-if-new-local-printer', except
3625that the definition occurs only when the local printer does not
3626already exists.
3627
3628Function `ses-define-if-new-local-printer' is not interactive, it
3629is intended for mode hooks to programatically automatically add
3630local printers."
3631 (unless (gethash name ses--local-printer-hashmap)
3632 (ses-define-local-printer name def)))
3587 3633
3588;;---------------------------------------------------------------------------- 3634;;----------------------------------------------------------------------------
3589;; Checking formulas for safety 3635;; Checking formulas for safety
@@ -3794,13 +3840,16 @@ either (ses-range BEG END) or (list ...). The TEST is evaluated."
3794;; Standard print functions 3840;; Standard print functions
3795;;---------------------------------------------------------------------------- 3841;;----------------------------------------------------------------------------
3796 3842
3797(defun ses-center (value &optional span fill) 3843(defun ses-center (value &optional span fill printer)
3798 "Print VALUE, centered within column. 3844 "Print VALUE, centered within column.
3799FILL is the fill character for centering (default = space). 3845FILL is the fill character for centering (default = space).
3800SPAN indicates how many additional rightward columns to include 3846SPAN indicates how many additional rightward columns to include
3801in width (default = 0)." 3847in width (default = 0).
3802 (let ((printer (or (ses-col-printer ses--col) ses--default-printer)) 3848PRINTER is the printer to use for printing the value, default is the
3803 (width (ses-col-width ses--col)) 3849column printer if any, or the spreadsheet the spreadsheet default
3850printer otherwise."
3851 (setq printer (or printer (ses-col-printer ses--col) ses--default-printer))
3852 (let ((width (ses-col-width ses--col))
3804 half) 3853 half)
3805 (or fill (setq fill ?\s)) 3854 (or fill (setq fill ?\s))
3806 (or span (setq span 0)) 3855 (or span (setq span 0))
@@ -3815,7 +3864,7 @@ in width (default = 0)."
3815 (concat half value half 3864 (concat half value half
3816 (if (> (% width 2) 0) (char-to-string fill)))))) 3865 (if (> (% width 2) 0) (char-to-string fill))))))
3817 3866
3818(defun ses-center-span (value &optional fill) 3867(defun ses-center-span (value &optional fill printer)
3819 "Print VALUE, centered within the span that starts in the current column 3868 "Print VALUE, centered within the span that starts in the current column
3820and continues until the next nonblank column. 3869and continues until the next nonblank column.
3821FILL specifies the fill character (default = space)." 3870FILL specifies the fill character (default = space)."
@@ -3823,22 +3872,28 @@ FILL specifies the fill character (default = space)."
3823 (while (and (< end ses--numcols) 3872 (while (and (< end ses--numcols)
3824 (memq (ses-cell-value ses--row end) '(nil *skip*))) 3873 (memq (ses-cell-value ses--row end) '(nil *skip*)))
3825 (setq end (1+ end))) 3874 (setq end (1+ end)))
3826 (ses-center value (- end ses--col 1) fill))) 3875 (ses-center value (- end ses--col 1) fill printer)))
3827 3876
3828(defun ses-dashfill (value &optional span) 3877(defun ses-dashfill (value &optional span printer)
3829 "Print VALUE centered using dashes. 3878 "Print VALUE centered using dashes.
3830SPAN indicates how many rightward columns to include in width (default = 0)." 3879SPAN indicates how many rightward columns to include in width (default = 0)."
3831 (ses-center value span ?-)) 3880 (ses-center value span ?- printer))
3832 3881
3833(defun ses-dashfill-span (value) 3882(defun ses-dashfill-span (value &optional printer)
3834 "Print VALUE, centered using dashes within the span that starts in the 3883 "Print VALUE, centered using dashes within the span that starts in the
3835current column and continues until the next nonblank column." 3884current column and continues until the next nonblank column."
3836 (ses-center-span value ?-)) 3885 (ses-center-span value ?- printer))
3837 3886
3838(defun ses-tildefill-span (value) 3887(defun ses-tildefill-span (value &optional printer)
3839 "Print VALUE, centered using tildes within the span that starts in the 3888 "Print VALUE, centered using tildes within the span that starts in the
3840current column and continues until the next nonblank column." 3889current column and continues until the next nonblank column."
3841 (ses-center-span value ?~)) 3890 (ses-center-span value ?~ printer))
3891
3892(defun ses-prin1 (value)
3893 "Shorthand for '(prin1-to-string VALUE t)'.
3894Usefull to handle the default behaviour in custom lambda based
3895printer functions."
3896 (prin1-to-string value t))
3842 3897
3843(defun ses-unsafe (_value) 3898(defun ses-unsafe (_value)
3844 "Substitute for an unsafe formula or printer." 3899 "Substitute for an unsafe formula or printer."