1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-01 08:17:38 +00:00

SES with case insensitive cell names for jumping.

* doc/misc/ses.texi (The Basics): Document that ses-jump may be
customized.
(Customizing @acronym{SES}): Document new customisations for ses-jump.

* lisp/ses.el (ses-jump-cell-name-function)
(ses-jump-prefix-function): New defcustoms.
(ses-jump-prefix): New defun.
(ses-jump): Make ses-jump use the new defcustoms.

* test/lisp/ses-tests.el (ses-jump-B2-prefix-arg)
(ses-jump-B2-lowcase, ses-jump-B2-lowcase-keys)
(ses-jump-B2-symbol, ses-jump-B2-renamed): New tests.
This commit is contained in:
Vincent Belaïche 2022-04-04 19:42:07 +02:00
parent 7a8798de95
commit f4b649ad0b
3 changed files with 137 additions and 19 deletions

View File

@ -220,7 +220,14 @@ You move around with the regular Emacs movement commands.
@table @kbd
@item j
Moves point to cell, specified by identifier (@code{ses-jump}).
Moves point to cell, specified by identifier (@code{ses-jump}). Unless
the cell is a renamed cell, the identifier is case-insensitive. A
prefix argument @math{n} move to cell with coordinates @math{(n\div R,
n \% C)} for a spreadsheet of @math{R} rows and @math{C} columns, and
A1 being of coordinates @math{(0,0)}. The way the identifier or the
command prefix argument are interpreted can be customized through
variables @code{ses-jump-cell-name-function} and
@code{ses-jump-prefix-function}.
@end table
Point is always at the left edge of a cell, or at the empty endline.
@ -726,10 +733,6 @@ yank. This doesn't make any difference?
@section Customizing @acronym{SES}
@cindex customizing
@vindex enable-local-eval
@vindex ses-mode-hook
@vindex safe-functions
@vindex enable-local-eval
By default, a newly-created spreadsheet has 1 row and 1 column. The
column width is 7 and the default printer is @samp{"%.7g"}. Each of these
@ -740,9 +743,34 @@ cell. You can customize @code{ses-after-entry-functions} to move left or
up or down. For diagonal movement, select two functions from the
list.
@vindex ses-jump-cell-name-function
@code{ses-jump-cell-name-function} is a customizable variable by
default set to the @code{upcase} function. This function is called
when you pass a cell name to the @command{ses-jump} command (@kbd{j}),
it changes the entered cell name to that where to jump. The default
setting @code{upcase} allows you to enter the cell name in low
case. Another use of @code{ses-jump-cell-name-function} could be some
internationalisation to convert non latin characters into latin
equivalents to name the cell. Instead of a cell name, the function may
return cell coordinates in the form of a cons, for instance @code{(0
. 0)} for cell @code{A1}, @code{(1 . 0)} for cell @code{A2}, etc.
@vindex ses-jump-prefix-function
@code{ses-jump-prefix-function} is a customisable variable by default
set to the @code{ses-jump-prefix} function. This function is called
when you give a prefix argument to the @command{ses-jump} command
(@kbd{j}). It returns a cell name or cell coordinates corresponding to
the prefix argument. Cell coordinates are in the form of a cons, for
instance @code{(1 . 0)} for cell @code{A2}. The default setting
@code{ses-jump-prefix} will number cells left to right and then top
down, so assuming a 4x3 spreadsheet prefix argument 0 jumps to cell
A1, prefix argument 2 jumps to C1, prefix argument 3 jumps to A2, etc.
@vindex ses-mode-hook
@code{ses-mode-hook} is a normal mode hook (list of functions to
execute when starting @acronym{SES} mode for a buffer).
@vindex safe-functions
The variable @code{safe-functions} is a list of possibly-unsafe
functions to be treated as safe when analyzing formulas and printers.
@xref{Virus protection}. Before customizing @code{safe-functions},

View File

@ -112,6 +112,24 @@ Each function is called with ARG=1."
:group 'ses
:type 'hook)
(defcustom ses-jump-cell-name-function 'upcase
"Function to process the string passed to function ses-jump. Set it to 'identity to make no change.
Set it to 'upcase to make cell name change case isensitive.
May return
* a string, in this case this must be a cell name.
* a (row . col) cons cell, in this case that must be valid cell coordinate."
:group 'ses
:type 'function)
(defcustom ses-jump-prefix-function 'ses-jump-prefix
"Function that takes the prefix argument passed to function ses-jump. It may return the same
sort of thing as ses-jump-cell-name-function."
:group 'ses
:type 'function)
;;----------------------------------------------------------------------------
;; Global variables and constants
@ -2233,24 +2251,41 @@ Based on the current set of columns and `window-hscroll' position."
;;----------------------------------------------------------------------------
;; Redisplay and recalculation
;;----------------------------------------------------------------------------
(defun ses-jump-prefix (prefix-int)
"Convert an integer into a (ROW . COL), by numbering cells starting from 0 from top left to bottom right, going row by row."
(and (>= prefix-int 0)
(< prefix-int (* ses--numcols ses--numrows))
(cons (/ prefix-int ses--numcols) (% prefix-int ses--numcols))))
(defun ses-jump (sym)
(defun ses-jump (&optional sym)
"Move point to cell SYM."
(interactive (let* (names
(s (completing-read
"Jump to cell: "
(and ses--named-cell-hashmap
(progn (maphash (lambda (key _val)
(push (symbol-name key) names))
ses--named-cell-hashmap)
names)))))
(if (string= s "")
(user-error "Invalid cell name")
(list (intern s)))))
(let ((rowcol (ses-sym-rowcol sym)))
(interactive "P")
(setq sym
(if current-prefix-arg
(funcall ses-jump-prefix-function (prefix-numeric-value sym))
(or sym
(completing-read
"Jump to cell: "
(and ses--named-cell-hashmap
(let (names)
(maphash (lambda (key _val)
(push (symbol-name key) names))
ses--named-cell-hashmap)
names))))))
(and (stringp sym)
(not (and ses--named-cell-hashmap (gethash (intern sym) ses--named-cell-hashmap)))
(setq sym (funcall ses-jump-cell-name-function sym)))
(if (stringp sym)
(if (string= sym "")
(user-error "Empty cell name")
(setq sym (intern sym))))
(let ((rowcol (if (consp sym)
(prog1 sym (setq sym (ses-cell-symbol (car sym) (cdr sym))))
(ses-sym-rowcol sym))))
(or rowcol (error "Invalid cell name"))
(if (eq (symbol-value sym) '*skip*)
(error "Cell is covered by preceding cell"))
(error "Cell is covered by preceding cell"))
(ses-goto-print (car rowcol) (cdr rowcol))))
(defun ses-jump-safe (cell)

View File

@ -178,6 +178,61 @@ to `ses--bar' and inserting a row, makes A2 value empty, and `ses--bar' equal to
(should (eq ses--bar 2)))))
;; JUMP tests
;; ======================================================================
(ert-deftest ses-jump-B2-prefix-arg ()
"Test jumping to cell B2 by use of prefix argument"
(let ((ses-initial-size '(3 . 3))
ses-after-entry-functions)
(with-temp-buffer
(ses-mode)
;; C-u 4 M-x ses-jump
(let ((current-prefix-arg 4))
(call-interactively 'ses-jump))
(should (eq (ses--cell-at-pos (point)) 'B2)))))
(ert-deftest ses-jump-B2-lowcase ()
"Test jumping to cell B2 by use of lowcase cell name string"
(let ((ses-initial-size '(3 . 3))
ses-after-entry-functions)
(with-temp-buffer
(ses-mode)
(funcall-interactively 'ses-jump "b2")
(ses-command-hook)
(should (eq (ses--cell-at-pos (point)) 'B2)))))
(ert-deftest ses-jump-B2-lowcase-keys ()
"Test jumping to cell B2 by use of lowcase cell name string with simulating keys"
(let ((ses-initial-size '(3 . 3))
ses-after-entry-functions)
(with-temp-buffer
(ses-mode)
(ert-simulate-keys [ ?b ?2 return] (ses-jump))
(ses-command-hook)
(should (eq (ses--cell-at-pos (point)) 'B2)))))
(ert-deftest ses-jump-B2-symbol ()
"Test jumping to cell B2 by use of cell name symbol"
(let ((ses-initial-size '(3 . 3))
ses-after-entry-functions)
(with-temp-buffer
(ses-mode)
(funcall-interactively 'ses-jump 'B2)
(ses-command-hook)
(should (eq (ses--cell-at-pos (point)) 'B2)))))
(ert-deftest ses-jump-B2-renamed ()
"Test jumping to cell B2 after renaming it `ses--toto'."
(let ((ses-initial-size '(3 . 3))
ses-after-entry-functions)
(with-temp-buffer
(ses-mode)
(ses-rename-cell 'ses--toto (ses-get-cell 1 1))
(ses-jump 'ses--toto)
(ses-command-hook)
(should (eq (ses--cell-at-pos (point)) 'ses--toto)))))
(provide 'ses-tests)
;;; ses-tests.el ends here