diff options
| author | Joost Kremers | 2024-05-07 11:52:27 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2024-05-18 11:52:33 +0300 |
| commit | 4f27d763bbe3312b6039fa59f2874bd8e002d201 (patch) | |
| tree | c31ffaac0bf29a103e229ee903b003267e52a90c | |
| parent | 19c983ddedf083f82008472c13dfd08ec94b615f (diff) | |
| download | emacs-4f27d763bbe3312b6039fa59f2874bd8e002d201.tar.gz emacs-4f27d763bbe3312b6039fa59f2874bd8e002d201.zip | |
Make 'vtable-insert-object' more versatile
* lisp/emacs-lisp/vtable.el (vtable-insert-object): Rename
argument AFTER-OBJECT to LOCATION; allow use of index to
refer to the insertion position; add argument BEFORE.
(Bug#70664).
* etc/NEWS:
* doc/misc/vtable.texi (Interface Functions): Document the
change.
* test/lisp/emacs-lisp/vtable-tests.el
(test-vtable-insert-object): New test.
| -rw-r--r-- | doc/misc/vtable.texi | 18 | ||||
| -rw-r--r-- | etc/NEWS | 13 | ||||
| -rw-r--r-- | lisp/emacs-lisp/vtable.el | 98 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/vtable-tests.el | 30 |
4 files changed, 132 insertions, 27 deletions
diff --git a/doc/misc/vtable.texi b/doc/misc/vtable.texi index dd5b70cf32f..822b1097cd9 100644 --- a/doc/misc/vtable.texi +++ b/doc/misc/vtable.texi | |||
| @@ -548,10 +548,20 @@ Remove @var{object} from @var{table}. This also updates the displayed | |||
| 548 | table. | 548 | table. |
| 549 | @end defun | 549 | @end defun |
| 550 | 550 | ||
| 551 | @defun vtable-insert-object table object &optional after-object | 551 | @defun vtable-insert-object table object &optional location before |
| 552 | Insert @var{object} into @var{table}. If @var{after-object}, insert | 552 | Insert @var{object} into @var{table}. @var{location} should be an |
| 553 | the object after this object; otherwise append to @var{table}. This | 553 | object in the table, the new object is inserted after this object, or |
| 554 | also updates the displayed table. | 554 | before it if @var{before} is non-nil. If @var{location} is @code{nil}, |
| 555 | @var{object} is appended to @var{table}, or prepended if @var{before} is | ||
| 556 | non-@code{nil}. | ||
| 557 | |||
| 558 | @var{location} can also be an integer, a zero-based index into the | ||
| 559 | table. In this case, @var{object} is inserted at that index. If the | ||
| 560 | index is out of range, @var{object} is prepended to @var{table} if the | ||
| 561 | index is too small, or appended if it is too large. In this case, | ||
| 562 | @var{before} is ignored. | ||
| 563 | |||
| 564 | This also updates the displayed table. | ||
| 555 | @end defun | 565 | @end defun |
| 556 | 566 | ||
| 557 | @defun vtable-update-object table object &optional old-object | 567 | @defun vtable-update-object table object &optional old-object |
| @@ -2635,6 +2635,19 @@ this case, would mean repeating the object in the argument list.) When | |||
| 2635 | replacing an object with a different one, passing both the new and old | 2635 | replacing an object with a different one, passing both the new and old |
| 2636 | objects is still necessary. | 2636 | objects is still necessary. |
| 2637 | 2637 | ||
| 2638 | ** 'vtable-insert-object' can insert "before" or at an index. | ||
| 2639 | The signature of 'vtable-insert-object' has changed and is now: | ||
| 2640 | |||
| 2641 | (vtable-insert-object table object &optional location before) | ||
| 2642 | |||
| 2643 | 'location' corresponds to the old 'after-object' argument; if 'before' | ||
| 2644 | is non-nil, the new object is inserted before the 'location' object, | ||
| 2645 | making it possible to insert a new object at the top of the | ||
| 2646 | table. (Before, this was not possible.) In addition, 'location' can be | ||
| 2647 | an integer, a (zero-based) index into the table at which the new object | ||
| 2648 | is inserted ('before' is ignored in this case). | ||
| 2649 | |||
| 2650 | |||
| 2638 | ** JSON | 2651 | ** JSON |
| 2639 | 2652 | ||
| 2640 | --- | 2653 | --- |
diff --git a/lisp/emacs-lisp/vtable.el b/lisp/emacs-lisp/vtable.el index d8e5136c666..cb7ea397314 100644 --- a/lisp/emacs-lisp/vtable.el +++ b/lisp/emacs-lisp/vtable.el | |||
| @@ -348,19 +348,57 @@ This will also remove the displayed line." | |||
| 348 | (when (vtable-goto-object object) | 348 | (when (vtable-goto-object object) |
| 349 | (delete-line))))) | 349 | (delete-line))))) |
| 350 | 350 | ||
| 351 | (defun vtable-insert-object (table object &optional after-object) | 351 | ;; FIXME: The fact that the `location' argument of |
| 352 | "Insert OBJECT into TABLE after AFTER-OBJECT. | 352 | ;; `vtable-insert-object' can be an integer and is then interpreted as |
| 353 | If AFTER-OBJECT is nil (or doesn't exist in the table), insert | 353 | ;; an index precludes the use of integers as objects. This seems a very |
| 354 | OBJECT at the end. | 354 | ;; unlikely use-case, so let's just accept this limitation. |
| 355 | |||
| 356 | (defun vtable-insert-object (table object &optional location before) | ||
| 357 | "Insert OBJECT into TABLE at LOCATION. | ||
| 358 | LOCATION is an object in TABLE. OBJECT is inserted after LOCATION, | ||
| 359 | unless BEFORE is non-nil, in which case it is inserted before LOCATION. | ||
| 360 | |||
| 361 | If LOCATION is nil, or does not exist in the table, OBJECT is inserted | ||
| 362 | at the end of the table, or at the beginning if BEFORE is non-nil. | ||
| 363 | |||
| 364 | LOCATION can also be an integer, a (zero-based) index into the table. | ||
| 365 | OBJECT is inserted at this location. If the index is out of range, | ||
| 366 | OBJECT is inserted at the beginning (if the index is less than 0) or | ||
| 367 | end (if the index is too large) of the table. BEFORE is ignored in this | ||
| 368 | case. | ||
| 369 | |||
| 355 | This also updates the displayed table." | 370 | This also updates the displayed table." |
| 371 | ;; FIXME: Inserting an object into an empty vtable currently isn't | ||
| 372 | ;; possible. `nconc' fails silently (twice), and `setcar' on the cache | ||
| 373 | ;; raises an error. | ||
| 374 | (if (null (vtable-objects table)) | ||
| 375 | (error "[vtable] Cannot insert object into empty vtable")) | ||
| 356 | ;; First insert into the objects. | 376 | ;; First insert into the objects. |
| 357 | (let (pos) | 377 | (let ((pos (if location |
| 358 | (if (and after-object | 378 | (if (integerp location) |
| 359 | (setq pos (memq after-object (vtable-objects table)))) | 379 | (prog1 |
| 360 | ;; Splice into list. | 380 | (nthcdr location (vtable-objects table)) |
| 361 | (setcdr pos (cons object (cdr pos))) | 381 | ;; Do not prepend if index is too large: |
| 362 | ;; Append. | 382 | (setq before nil)) |
| 363 | (nconc (vtable-objects table) (list object)))) | 383 | (or (memq location (vtable-objects table)) |
| 384 | ;; Prepend if `location' is not found and | ||
| 385 | ;; `before' is non-nil: | ||
| 386 | (and before (vtable-objects table)))) | ||
| 387 | ;; If `location' is nil and `before' is non-nil, we | ||
| 388 | ;; prepend the new object. | ||
| 389 | (if before (vtable-objects table))))) | ||
| 390 | (if (or before ; If `before' is non-nil, `pos' should be, as well. | ||
| 391 | (and pos (integerp location))) | ||
| 392 | ;; Add the new object before. | ||
| 393 | (let ((old-object (car pos))) | ||
| 394 | (setcar pos object) | ||
| 395 | (setcdr pos (cons old-object (cdr pos)))) | ||
| 396 | ;; Otherwise, add the object after. | ||
| 397 | (if pos | ||
| 398 | ;; Splice the object into the list. | ||
| 399 | (setcdr pos (cons object (cdr pos))) | ||
| 400 | ;; Otherwise, append the object. | ||
| 401 | (nconc (vtable-objects table) (list object))))) | ||
| 364 | ;; Then adjust the cache and display. | 402 | ;; Then adjust the cache and display. |
| 365 | (save-excursion | 403 | (save-excursion |
| 366 | (vtable-goto-table table) | 404 | (vtable-goto-table table) |
| @@ -372,19 +410,33 @@ This also updates the displayed table." | |||
| 372 | 'face (vtable-face table)) | 410 | 'face (vtable-face table)) |
| 373 | "")) | 411 | "")) |
| 374 | (ellipsis-width (string-pixel-width ellipsis)) | 412 | (ellipsis-width (string-pixel-width ellipsis)) |
| 375 | (elem (and after-object | 413 | (elem (if location ; This binding mirrors the binding of `pos' above. |
| 376 | (assq after-object (car cache)))) | 414 | (if (integerp location) |
| 415 | (nth location (car cache)) | ||
| 416 | (or (assq location (car cache)) | ||
| 417 | (and before (caar cache)))) | ||
| 418 | (if before (caar cache)))) | ||
| 419 | (pos (memq elem (car cache))) | ||
| 377 | (line (cons object (vtable--compute-cached-line table object)))) | 420 | (line (cons object (vtable--compute-cached-line table object)))) |
| 378 | (if (not elem) | 421 | (if (or before |
| 379 | ;; Append. | 422 | (and pos (integerp location))) |
| 380 | (progn | 423 | ;; Add the new object before:. |
| 381 | (setcar cache (nconc (car cache) (list line))) | 424 | (let ((old-line (car pos))) |
| 382 | (vtable-end-of-table)) | 425 | (setcar pos line) |
| 383 | ;; Splice into list. | 426 | (setcdr pos (cons old-line (cdr pos))) |
| 384 | (let ((pos (memq elem (car cache)))) | 427 | (unless (vtable-goto-object (car elem)) |
| 385 | (setcdr pos (cons line (cdr pos))) | 428 | (vtable-beginning-of-table))) |
| 386 | (unless (vtable-goto-object after-object) | 429 | ;; Otherwise, add the object after. |
| 387 | (vtable-end-of-table)))) | 430 | (if pos |
| 431 | ;; Splice the object into the list. | ||
| 432 | (progn | ||
| 433 | (setcdr pos (cons line (cdr pos))) | ||
| 434 | (if (vtable-goto-object location) | ||
| 435 | (forward-line 1) ; Insert *after*. | ||
| 436 | (vtable-end-of-table))) | ||
| 437 | ;; Otherwise, append the object. | ||
| 438 | (setcar cache (nconc (car cache) (list line))) | ||
| 439 | (vtable-end-of-table))) | ||
| 388 | (let ((start (point))) | 440 | (let ((start (point))) |
| 389 | ;; FIXME: We have to adjust colors in lines below this if we | 441 | ;; FIXME: We have to adjust colors in lines below this if we |
| 390 | ;; have :row-colors. | 442 | ;; have :row-colors. |
diff --git a/test/lisp/emacs-lisp/vtable-tests.el b/test/lisp/emacs-lisp/vtable-tests.el index 08fdf1594a4..1d4b0650210 100644 --- a/test/lisp/emacs-lisp/vtable-tests.el +++ b/test/lisp/emacs-lisp/vtable-tests.el | |||
| @@ -39,4 +39,34 @@ | |||
| 39 | :insert nil))) | 39 | :insert nil))) |
| 40 | '(left right left)))) | 40 | '(left right left)))) |
| 41 | 41 | ||
| 42 | (ert-deftest test-vtable-insert-object () | ||
| 43 | (should | ||
| 44 | (equal (let ((buffer (get-buffer-create " *vtable-test*"))) | ||
| 45 | (pop-to-buffer buffer) | ||
| 46 | (erase-buffer) | ||
| 47 | (let* ((object1 '("Foo" 3)) | ||
| 48 | (object2 '("Gazonk" 8)) | ||
| 49 | (table (make-vtable | ||
| 50 | :columns '("Name" (:name "Rank" :width 5)) | ||
| 51 | :objects (list object1 object2)))) | ||
| 52 | (mapc (lambda (args) | ||
| 53 | (pcase-let ((`(,object ,location ,before) args)) | ||
| 54 | (vtable-insert-object table object location before))) | ||
| 55 | `( ; Some correct inputs. | ||
| 56 | ;; object location before | ||
| 57 | (("Fizz" 4) ,object1 nil) | ||
| 58 | (("Bop" 7) ,object2 t) | ||
| 59 | (("Zat" 5) 2 nil) | ||
| 60 | (("Dib" 6) 3 t) | ||
| 61 | (("Wup" 9) nil nil) | ||
| 62 | (("Quam" 2) nil t) | ||
| 63 | ;; And some faulty inputs. | ||
| 64 | (("Yat" 1) -1 nil) ; non-existing index, `before' is ignored. | ||
| 65 | (("Vop" 10) 100 t) ; non-existing index, `before' is ignored. | ||
| 66 | (("Jib" 11) ("Bleh" 0) nil) ; non-existing object. | ||
| 67 | (("Nix" 0) ("Ugh" 0) t) ; non-existing object. | ||
| 68 | )) | ||
| 69 | (mapcar #'cadr (vtable-objects table)))) | ||
| 70 | (number-sequence 0 11)))) | ||
| 71 | |||
| 42 | ;;; vtable-tests.el ends here | 72 | ;;; vtable-tests.el ends here |