aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/emacs-lisp/vtable.el321
-rw-r--r--test/lisp/emacs-lisp/vtable-tests.el91
2 files changed, 263 insertions, 149 deletions
diff --git a/lisp/emacs-lisp/vtable.el b/lisp/emacs-lisp/vtable.el
index bcdd280fb92..29791e2aaa0 100644
--- a/lisp/emacs-lisp/vtable.el
+++ b/lisp/emacs-lisp/vtable.el
@@ -67,8 +67,10 @@
67 (ellipsis :initarg :ellipsis :accessor vtable-ellipsis) 67 (ellipsis :initarg :ellipsis :accessor vtable-ellipsis)
68 (column-colors :initarg :column-colors :accessor vtable-column-colors) 68 (column-colors :initarg :column-colors :accessor vtable-column-colors)
69 (row-colors :initarg :row-colors :accessor vtable-row-colors) 69 (row-colors :initarg :row-colors :accessor vtable-row-colors)
70 (buffer :initform nil :accessor vtable-buffer)
70 (-cached-colors :initform nil) 71 (-cached-colors :initform nil)
71 (-cache :initform (make-hash-table :test #'equal)) 72 (-cache :initform (make-hash-table :test #'equal))
73 (-current-cache :initform nil :accessor vtable--current-cache)
72 (-cached-keymap :initform nil) 74 (-cached-keymap :initform nil)
73 (-has-column-spec :initform nil)) 75 (-has-column-spec :initform nil))
74 "An object to hold the data for a table.") 76 "An object to hold the data for a table.")
@@ -287,8 +289,7 @@ compared with `equal'), signal an error.
287TABLE must be at point in the current buffer." 289TABLE must be at point in the current buffer."
288 (unless old-object 290 (unless old-object
289 (setq old-object object)) 291 (setq old-object object))
290 (let* ((objects (vtable-objects table)) 292 (let ((objects (vtable-objects table)))
291 (inhibit-read-only t))
292 ;; First replace the object in the object storage. 293 ;; First replace the object in the object storage.
293 (if (eq old-object (car objects)) 294 (if (eq old-object (car objects))
294 ;; It's at the head, so replace it there. 295 ;; It's at the head, so replace it there.
@@ -301,31 +302,32 @@ TABLE must be at point in the current buffer."
301 (unless (cdr objects) 302 (unless (cdr objects)
302 (error "Can't find the old object")) 303 (error "Can't find the old object"))
303 (setcar (cdr objects) object)) 304 (setcar (cdr objects) object))
304 ;; Then update the rendered vtable in the current buffer. 305 ;; Then update the rendered vtable in its buffer.
305 (if-let* ((cache (vtable--current-cache)) 306 (if-let* ((cache (vtable--current-cache table))
306 (line-number (seq-position (vtable--cache-lines cache) 307 (line-number (seq-position (vtable--cache-lines cache)
307 old-object 308 old-object
308 (lambda (a b) 309 (lambda (a b)
309 (equal (car a) b)))) 310 (equal (car a) b))))
310 (line (elt (vtable--cache-lines cache) line-number))) 311 (line (elt (vtable--cache-lines cache) line-number)))
311 (progn 312 (with-current-buffer (vtable-buffer table)
312 (setcar line object) 313 (let ((inhibit-read-only t)
313 (setcdr line (vtable--compute-cached-line table object)) 314 (inhibit-modification-hooks t))
314 ;; ... and redisplay the line in question. 315 (setcar line object)
315 (save-excursion 316 (setcdr line (vtable--compute-cached-line table object))
316 (vtable-goto-object old-object) 317 ;; ... and redisplay the line in question.
317 (let ((keymap (get-text-property (point) 'keymap)) 318 (save-excursion
318 (start (point))) 319 (vtable-goto-object old-object)
319 (delete-line) 320 (let ((keymap (get-text-property (point) 'keymap))
320 (vtable--insert-line table line line-number 321 (start (point)))
321 (vtable--cache-widths cache) 322 (delete-line)
322 (vtable--spacer table)) 323 (vtable--insert-line table line line-number
323 (add-text-properties start (point) (list 'keymap keymap 324 (vtable--cache-widths cache)
324 'vtable table 325 (vtable--spacer table))
325 'vtable-cache cache)))) 326 (add-text-properties start (point) (list 'keymap keymap
326 ;; We may have inserted a non-numerical value into a previously 327 'vtable table))))
327 ;; all-numerical table, so recompute. 328 ;; We may have inserted a non-numerical value into a previously
328 (vtable--recompute-numerical table (cdr line))) 329 ;; all-numerical table, so recompute.
330 (vtable--recompute-numerical table (cdr line))))
329 (error "Can't find cached object in vtable")))) 331 (error "Can't find cached object in vtable"))))
330 332
331(defun vtable-remove-object (table object) 333(defun vtable-remove-object (table object)
@@ -334,14 +336,16 @@ This will also remove the displayed line."
334 ;; First remove from the objects. 336 ;; First remove from the objects.
335 (setf (vtable-objects table) (delq object (vtable-objects table))) 337 (setf (vtable-objects table) (delq object (vtable-objects table)))
336 ;; Then adjust the cache and display. 338 ;; Then adjust the cache and display.
337 (save-excursion 339 (with-current-buffer (vtable-buffer table)
338 (vtable-goto-table table) 340 (save-excursion
339 (let ((cache (vtable--current-cache)) 341 (vtable-goto-table table)
340 (inhibit-read-only t)) 342 (let ((cache (vtable--current-cache table))
341 (setcar cache (delq (assq object (vtable--cache-lines cache)) 343 (inhibit-read-only t)
342 (vtable--cache-lines cache))) 344 (inhibit-modification-hooks t))
343 (when (vtable-goto-object object) 345 (setcar cache (delq (assq object (vtable--cache-lines cache))
344 (delete-line))))) 346 (vtable--cache-lines cache)))
347 (when (vtable-goto-object object)
348 (delete-line))))))
345 349
346;; FIXME: The fact that the `location' argument of 350;; FIXME: The fact that the `location' argument of
347;; `vtable-insert-object' can be an integer and is then interpreted as 351;; `vtable-insert-object' can be an integer and is then interpreted as
@@ -369,8 +373,9 @@ This also updates the displayed table."
369 (progn 373 (progn
370 (setf (vtable-objects table) (list object)) 374 (setf (vtable-objects table) (list object))
371 (vtable--recompute-numerical table (vtable--compute-cached-line table object)) 375 (vtable--recompute-numerical table (vtable--compute-cached-line table object))
372 (vtable-goto-table table) 376 (with-current-buffer (vtable-buffer table)
373 (vtable-revert-command)) 377 (vtable-goto-table table)
378 (vtable-revert-command)))
374 ;; First insert into the objects. 379 ;; First insert into the objects.
375 (let ((pos (if location 380 (let ((pos (if location
376 (if (integerp location) 381 (if (integerp location)
@@ -398,56 +403,58 @@ This also updates the displayed table."
398 ;; Otherwise, append the object. 403 ;; Otherwise, append the object.
399 (nconc (vtable-objects table) (list object))))) 404 (nconc (vtable-objects table) (list object)))))
400 ;; Then adjust the cache and display. 405 ;; Then adjust the cache and display.
401 (save-excursion 406 (let* ((cache (vtable--current-cache table))
402 (vtable-goto-table table) 407 (lines (vtable--cache-lines cache))
403 (let* ((cache (vtable--current-cache)) 408 (elem (if location ; This binding mirrors the binding of `pos' above.
404 (inhibit-read-only t) 409 (if (integerp location)
405 (keymap (get-text-property (point) 'keymap)) 410 (nth location lines)
406 (ellipsis (if (vtable-ellipsis table) 411 (or (assq location lines)
407 (propertize (truncate-string-ellipsis) 412 (and before (car lines))))
408 'face (vtable-face table)) 413 (if before (car lines))))
409 "")) 414 (pos (memq elem lines))
410 (ellipsis-width (string-pixel-width ellipsis)) 415 (line (cons object (vtable--compute-cached-line table object))))
411 (lines (vtable--cache-lines cache)) 416 (with-current-buffer (vtable-buffer table)
412 (elem (if location ; This binding mirrors the binding of `pos' above. 417 (let ((inhibit-read-only t)
413 (if (integerp location) 418 (inhibit-modification-hooks t))
414 (nth location lines) 419 (save-excursion
415 (or (assq location lines) 420 (vtable-goto-table table)
416 (and before (car lines)))) 421 (if (or before
417 (if before (car lines)))) 422 (and pos (integerp location)))
418 (pos (memq elem lines)) 423 ;; Add the new object before:.
419 (line (cons object (vtable--compute-cached-line table object)))) 424 (let ((old-line (car pos)))
420 (if (or before 425 (setcar pos line)
421 (and pos (integerp location))) 426 (setcdr pos (cons old-line (cdr pos)))
422 ;; Add the new object before:. 427 (unless (vtable-goto-object (car elem))
423 (let ((old-line (car pos))) 428 (vtable-beginning-of-table)))
424 (setcar pos line) 429 ;; Otherwise, add the object after.
425 (setcdr pos (cons old-line (cdr pos))) 430 (if pos
426 (unless (vtable-goto-object (car elem)) 431 ;; Splice the object into the list.
427 (vtable-beginning-of-table))) 432 (progn
428 ;; Otherwise, add the object after. 433 (setcdr pos (cons line (cdr pos)))
429 (if pos 434 (if (vtable-goto-object location)
430 ;; Splice the object into the list. 435 (forward-line 1) ; Insert *after*.
431 (progn 436 (vtable-end-of-table)))
432 (setcdr pos (cons line (cdr pos))) 437 ;; Otherwise, append the object.
433 (if (vtable-goto-object location) 438 (setcar cache (nconc lines (list line)))
434 (forward-line 1) ; Insert *after*. 439 (vtable-end-of-table)))
435 (vtable-end-of-table))) 440 (let* ((start (point))
436 ;; Otherwise, append the object. 441 (ellipsis (if (vtable-ellipsis table)
437 (setcar cache (nconc lines (list line))) 442 (propertize (truncate-string-ellipsis)
438 (vtable-end-of-table))) 443 'face (vtable-face table))
439 (let ((start (point))) 444 ""))
440 ;; FIXME: We have to adjust colors in lines below this if we 445 (ellipsis-width (string-pixel-width ellipsis (current-buffer)))
441 ;; have :row-colors. 446 (keymap (get-text-property (point) 'keymap)))
442 (vtable--insert-line table line 0 447 ;; FIXME: We have to adjust colors in lines below this if we
443 (vtable--cache-widths cache) (vtable--spacer table) 448 ;; have :row-colors.
444 ellipsis ellipsis-width) 449 (vtable--insert-line table line 0
445 (add-text-properties start (point) (list 'keymap keymap 450 (vtable--cache-widths cache)
446 'vtable table 451 (vtable--spacer table)
447 'vtable-cache cache))) 452 ellipsis ellipsis-width)
448 ;; We may have inserted a non-numerical value into a previously 453 (add-text-properties start (point) (list 'keymap keymap
449 ;; all-numerical table, so recompute. 454 'vtable table)))
450 (vtable--recompute-numerical table (cdr line)))))) 455 ;; We may have inserted a non-numerical value into a previously
456 ;; all-numerical table, so recompute.
457 (vtable--recompute-numerical table (cdr line))))))))
451 458
452(defun vtable-column (table index) 459(defun vtable-column (table index)
453 "Return the name of the INDEXth column in TABLE." 460 "Return the name of the INDEXth column in TABLE."
@@ -520,14 +527,14 @@ recompute the column specs when the table data has changed."
520(defun vtable--cache-lines (cache) 527(defun vtable--cache-lines (cache)
521 (car cache)) 528 (car cache))
522 529
523(defun vtable-insert (table) 530(defun vtable--insert (table)
524 (let* ((spacer (vtable--spacer table)) 531 (let* ((spacer (vtable--spacer table))
525 (start (point)) 532 (start (point))
526 (ellipsis (if (vtable-ellipsis table) 533 (ellipsis (if (vtable-ellipsis table)
527 (propertize (truncate-string-ellipsis) 534 (propertize (truncate-string-ellipsis)
528 'face (vtable-face table)) 535 'face (vtable-face table))
529 "")) 536 ""))
530 (ellipsis-width (string-pixel-width ellipsis)) 537 (ellipsis-width (string-pixel-width ellipsis (vtable-buffer table)))
531 ;; We maintain a cache per screen/window width, so that we render 538 ;; We maintain a cache per screen/window width, so that we render
532 ;; correctly if Emacs is open on two different screens (or the 539 ;; correctly if Emacs is open on two different screens (or the
533 ;; user resizes the frame). 540 ;; user resizes the frame).
@@ -549,8 +556,7 @@ recompute the column specs when the table data has changed."
549 (add-text-properties start (point) 556 (add-text-properties start (point)
550 (list 'keymap vtable-header-line-map 557 (list 'keymap vtable-header-line-map
551 'rear-nonsticky t 558 'rear-nonsticky t
552 'vtable table 559 'vtable table))
553 'vtable-cache cache))
554 (setq start (point)))) 560 (setq start (point))))
555 (vtable--sort table cache) 561 (vtable--sort table cache)
556 ;; Insert the data. 562 ;; Insert the data.
@@ -561,13 +567,35 @@ recompute the column specs when the table data has changed."
561 (setq line-number (1+ line-number)))) 567 (setq line-number (1+ line-number))))
562 (add-text-properties start (point) 568 (add-text-properties start (point)
563 (list 'rear-nonsticky t 569 (list 'rear-nonsticky t
564 'vtable table 570 'vtable table))
565 'vtable-cache cache)) 571 (setf (vtable--current-cache table) cache)
566 (goto-char start))) 572 (goto-char start)))
567 573
574(defun vtable-insert (table)
575 "Insert TABLE into the current buffer.
576The current buffer will be recorded as TABLE's buffer. If the table is
577inserted into a buffer other than its originating buffer, signal an
578error. A table may be reinserted into its own buffer, but insert only
579one instance per buffer. This restriction needs to be enforced by the
580caller."
581 (if-let* ((table-buffer (vtable-buffer table)))
582 (when (not (eq table-buffer (current-buffer)))
583 (error "A vtable cannot be inserted into more than one buffer")))
584 (setf (vtable-buffer table) (current-buffer))
585 (let ((inhibit-read-only t)
586 (inhibit-modification-hooks t))
587 (vtable--insert table)))
588
589(defun vtable-set-buffer (table buffer)
590 "Associate BUFFER with TABLE.
591Use this function with care, and ensure your vtable instance renders
592itself in the new buffer."
593 (setf (vtable-buffer table) buffer))
594
568(defun vtable--insert-line (table line line-number widths spacer 595(defun vtable--insert-line (table line line-number widths spacer
569 &optional ellipsis ellipsis-width) 596 &optional ellipsis ellipsis-width)
570 (let ((start (point)) 597 (let ((start (point))
598 (buffer (vtable-buffer table))
571 (columns (vtable-columns table)) 599 (columns (vtable-columns table))
572 (column-colors 600 (column-colors
573 (and (vtable-column-colors table) 601 (and (vtable-column-colors table)
@@ -607,16 +635,18 @@ recompute the column specs when the table data has changed."
607 (concat 635 (concat
608 (vtable--limit-string 636 (vtable--limit-string
609 pre-computed (- (elt widths index) 637 pre-computed (- (elt widths index)
610 (or ellipsis-width 0))) 638 (or ellipsis-width 0))
639 buffer)
611 ellipsis) 640 ellipsis)
612 pre-computed)) 641 pre-computed))
613 ;; Recompute widths. 642 ;; Recompute widths.
614 (t 643 (t
615 (if (> (string-pixel-width value) (elt widths index)) 644 (if (> (string-pixel-width value buffer) (elt widths index))
616 (concat 645 (concat
617 (vtable--limit-string 646 (vtable--limit-string
618 value (- (elt widths index) 647 value (- (elt widths index)
619 (or ellipsis-width 0))) 648 (or ellipsis-width 0))
649 buffer)
620 ellipsis) 650 ellipsis)
621 value)))) 651 value))))
622 (start (point)) 652 (start (point))
@@ -630,14 +660,15 @@ recompute the column specs when the table data has changed."
630 (list 'space 660 (list 'space
631 :width (list 661 :width (list
632 (+ (- (elt widths index) 662 (+ (- (elt widths index)
633 (string-pixel-width displayed)) 663 (string-pixel-width
664 displayed buffer))
634 (if last 0 spacer))))))) 665 (if last 0 spacer)))))))
635 ;; Align to the right. 666 ;; Align to the right.
636 (insert (propertize " " 'display 667 (insert (propertize " " 'display
637 (list 'space 668 (list 'space
638 :width (list (- (elt widths index) 669 :width (list (- (elt widths index)
639 (string-pixel-width 670 (string-pixel-width
640 displayed))))) 671 displayed buffer)))))
641 displayed) 672 displayed)
642 (unless last 673 (unless last
643 (insert (propertize " " 'display 674 (insert (propertize " " 'display
@@ -664,16 +695,6 @@ recompute the column specs when the table data has changed."
664(defun vtable--cache-key () 695(defun vtable--cache-key ()
665 (cons (frame-terminal) (window-width))) 696 (cons (frame-terminal) (window-width)))
666 697
667(defun vtable--current-cache ()
668 "Return the current cache for the table at point.
669
670In `vtable-insert', the lines and widths of the vtable text are computed
671based on the current selected frame and window and stored in a cache.
672Subsequent interaction with the text of the vtable should use that cache
673via this function rather than by calling `vtable--cache-key' to look up
674the cache."
675 (get-text-property (point) 'vtable-cache))
676
677(defun vtable--clear-cache (table) 698(defun vtable--clear-cache (table)
678 (setf (gethash (vtable--cache-key) (slot-value table '-cache)) nil)) 699 (setf (gethash (vtable--cache-key) (slot-value table '-cache)) nil))
679 700
@@ -718,6 +739,7 @@ the cache."
718(defun vtable--insert-header-line (table widths spacer) 739(defun vtable--insert-header-line (table widths spacer)
719 ;; Insert the header directly into the buffer. 740 ;; Insert the header directly into the buffer.
720 (let ((start (point)) 741 (let ((start (point))
742 (buffer (vtable-buffer table))
721 (divider (vtable-divider table)) 743 (divider (vtable-divider table))
722 (cmap (define-keymap 744 (cmap (define-keymap
723 "<header-line> <drag-mouse-1>" #'vtable--drag-resize-column 745 "<header-line> <drag-mouse-1>" #'vtable--drag-resize-column
@@ -737,14 +759,15 @@ the cache."
737 'keymap cmap)) 759 'keymap cmap))
738 (start (point)) 760 (start (point))
739 (indicator (vtable--indicator table index)) 761 (indicator (vtable--indicator table index))
740 (indicator-width (string-pixel-width indicator)) 762 (indicator-width (string-pixel-width indicator buffer))
741 (last (= index (1- (length (vtable-columns table))))) 763 (last (= index (1- (length (vtable-columns table)))))
742 displayed) 764 displayed)
743 (setq displayed 765 (setq displayed
744 (if (> (string-pixel-width name) 766 (if (> (string-pixel-width name buffer)
745 (- (elt widths index) indicator-width)) 767 (- (elt widths index) indicator-width))
746 (vtable--limit-string 768 (vtable--limit-string
747 name (- (elt widths index) indicator-width)) 769 name (- (elt widths index) indicator-width)
770 buffer)
748 name)) 771 name))
749 (let* ((indicator-lead-width 772 (let* ((indicator-lead-width
750 ;; We want the indicator to not be quite flush right. 773 ;; We want the indicator to not be quite flush right.
@@ -753,7 +776,7 @@ the cache."
753 indicator-lead-width)) 776 indicator-lead-width))
754 (fill-width 777 (fill-width
755 (+ (- (elt widths index) 778 (+ (- (elt widths index)
756 (string-pixel-width displayed) 779 (string-pixel-width displayed buffer)
757 indicator-width 780 indicator-width
758 indicator-lead-width) 781 indicator-lead-width)
759 (if last 0 spacer)))) 782 (if last 0 spacer))))
@@ -771,7 +794,8 @@ the cache."
771 ;; This is the final column, and we have a sorting 794 ;; This is the final column, and we have a sorting
772 ;; indicator, and the table is too wide for the window. 795 ;; indicator, and the table is too wide for the window.
773 (let* ((pre-indicator (string-pixel-width 796 (let* ((pre-indicator (string-pixel-width
774 (buffer-substring (point-min) (point)))) 797 (buffer-substring (point-min) (point))
798 buffer))
775 (pre-fill 799 (pre-fill
776 (- (window-width nil t) 800 (- (window-width nil t)
777 pre-indicator 801 pre-indicator
@@ -850,14 +874,16 @@ If NEXT, do the next column."
850 (buffer-substring (point-min) (1- (point-max)))))) 874 (buffer-substring (point-min) (1- (point-max))))))
851 (vtable-header-mode 1)) 875 (vtable-header-mode 1))
852 876
853(defun vtable--limit-string (string pixels) 877
878(defun vtable--limit-string (string pixels buffer)
854 (while (and (length> string 0) 879 (while (and (length> string 0)
855 (> (string-pixel-width string) pixels)) 880 (> (string-pixel-width string buffer) pixels))
856 (setq string (substring string 0 (1- (length string))))) 881 (setq string (substring string 0 (1- (length string)))))
857 string) 882 string)
858 883
859(defun vtable--char-width (table) 884(defun vtable--char-width (table)
860 (string-pixel-width (propertize "x" 'face (vtable-face table)))) 885 (string-pixel-width (propertize "x" 'face (vtable-face table))
886 (vtable-buffer table)))
861 887
862(defun vtable--compute-width (table spec) 888(defun vtable--compute-width (table spec)
863 (cond 889 (cond
@@ -936,7 +962,7 @@ CACHE is TABLE's cache data as returned by `vtable--compute-cache'."
936 ;; We stash the computed width and string here -- if there are 962 ;; We stash the computed width and string here -- if there are
937 ;; no formatters/displayers, we'll be using the string, and 963 ;; no formatters/displayers, we'll be using the string, and
938 ;; then won't have to recreate it. 964 ;; then won't have to recreate it.
939 (list value (string-pixel-width string) string))) 965 (list value (string-pixel-width string (vtable-buffer table)) string)))
940 (vtable-columns table))) 966 (vtable-columns table)))
941 967
942(defun vtable--make-keymap (table) 968(defun vtable--make-keymap (table)
@@ -967,20 +993,22 @@ CACHE is TABLE's cache data as returned by `vtable--compute-cache'."
967 (vtable-keymap table)) 993 (vtable-keymap table))
968 map))) 994 map)))
969 995
970(defun vtable-revert () 996(defun vtable-revert (&optional table)
971 "Regenerate the table under point." 997 "Regenerate TABLE, defaulting to the table under point."
972 (let ((table (vtable-current-table)) 998 (setq table (or table (vtable-current-table)))
973 (object (vtable-current-object)) 999 (unless table
974 (column (vtable-current-column)) 1000 (user-error "No table under point"))
975 (inhibit-read-only t)) 1001 (with-current-buffer (vtable-buffer table)
976 (unless table 1002 (let ((object (vtable-current-object))
977 (user-error "No table under point")) 1003 (column (vtable-current-column))
978 (delete-region (vtable-beginning-of-table) (vtable-end-of-table)) 1004 (inhibit-read-only t)
979 (vtable-insert table) 1005 (inhibit-modification-hooks t))
980 (when object 1006 (delete-region (vtable-beginning-of-table) (vtable-end-of-table))
981 (vtable-goto-object object)) 1007 (vtable--insert table)
982 (when column 1008 (when object
983 (vtable-goto-column column)))) 1009 (vtable-goto-object object))
1010 (when column
1011 (vtable-goto-column column)))))
984 1012
985;;; Commands. 1013;;; Commands.
986 1014
@@ -1006,14 +1034,14 @@ Interactively, N is the prefix argument."
1006 (- (* (vtable--char-width table) (or n 1)))))) 1034 (- (* (vtable--char-width table) (or n 1))))))
1007 1035
1008(defun vtable--alter-column-width (table column delta) 1036(defun vtable--alter-column-width (table column delta)
1009 (let ((widths (vtable--cache-widths (vtable--current-cache)))) 1037 (let ((widths (vtable--cache-widths (vtable--current-cache table))))
1010 (setf (aref widths column) 1038 (setf (aref widths column)
1011 (max (* (vtable--char-width table) 2) 1039 (max (* (vtable--char-width table) 2)
1012 (+ (aref widths column) delta))) 1040 (+ (aref widths column) delta)))
1013 ;; Store the width so it'll be respected on a revert. 1041 ;; Store the width so it'll be respected on a revert.
1014 (setf (vtable-column-width (elt (vtable-columns table) column)) 1042 (setf (vtable-column-width (elt (vtable-columns table) column))
1015 (format "%dpx" (aref widths column))) 1043 (format "%dpx" (aref widths column)))
1016 (vtable-revert))) 1044 (vtable-revert table)))
1017 1045
1018(defun vtable-widen-current-column (&optional n) 1046(defun vtable-widen-current-column (&optional n)
1019 "Widen the current column by N characters. 1047 "Widen the current column by N characters.
@@ -1028,24 +1056,29 @@ Interactively, N is the prefix argument."
1028 (interactive) 1056 (interactive)
1029 (vtable-goto-column 1057 (vtable-goto-column
1030 (max 0 (1- (or (vtable-current-column) 1058 (max 0 (1- (or (vtable-current-column)
1031 (length (vtable--cache-widths (vtable--current-cache)))))))) 1059 (length (vtable--cache-widths
1060 (vtable--current-cache (vtable-current-table)))))))))
1032 1061
1033(defun vtable-next-column () 1062(defun vtable-next-column ()
1034 "Go to the next column." 1063 "Go to the next column."
1035 (interactive) 1064 (interactive)
1036 (when (vtable-current-column) 1065 (when (vtable-current-column)
1037 (vtable-goto-column 1066 (vtable-goto-column
1038 (min (1- (length (vtable--cache-widths (vtable--current-cache)))) 1067 (min (1- (length (vtable--cache-widths
1068 (vtable--current-cache (vtable-current-table)))))
1039 (1+ (vtable-current-column)))))) 1069 (1+ (vtable-current-column))))))
1040 1070
1041(defun vtable-revert-command () 1071(defun vtable-revert-command (&optional table)
1042 "Re-query data and regenerate the table under point." 1072 "Re-query data and regenerate TABLE.
1073If TABLE is nil, use the table under point."
1043 (interactive) 1074 (interactive)
1044 (let ((table (vtable-current-table))) 1075 (setq table (or table (vtable-current-table)))
1045 (when (vtable-objects-function table) 1076 (unless table
1046 (setf (vtable-objects table) (funcall (vtable-objects-function table)))) 1077 (user-error "No table found"))
1047 (vtable--clear-cache table)) 1078 (when (vtable-objects-function table)
1048 (vtable-revert)) 1079 (setf (vtable-objects table) (funcall (vtable-objects-function table))))
1080 (vtable--clear-cache table)
1081 (vtable-revert table))
1049 1082
1050(defun vtable-sort-by-current-column () 1083(defun vtable-sort-by-current-column ()
1051 "Sort the table under point by the column under point." 1084 "Sort the table under point by the column under point."
@@ -1067,8 +1100,8 @@ Interactively, N is the prefix argument."
1067 (if (eq (cdr last) 'ascend) 1100 (if (eq (cdr last) 'ascend)
1068 'descend 1101 'descend
1069 'ascend) 1102 'ascend)
1070 'ascend)))))) 1103 'ascend)))))
1071 (vtable-revert)) 1104 (vtable-revert table)))
1072 1105
1073(defun vtable-header-line-sort (e) 1106(defun vtable-header-line-sort (e)
1074 "Sort a vtable from the header line." 1107 "Sort a vtable from the header line."
diff --git a/test/lisp/emacs-lisp/vtable-tests.el b/test/lisp/emacs-lisp/vtable-tests.el
index 74fb8cc8139..f865206dc19 100644
--- a/test/lisp/emacs-lisp/vtable-tests.el
+++ b/test/lisp/emacs-lisp/vtable-tests.el
@@ -27,16 +27,19 @@
27(require 'ert) 27(require 'ert)
28(require 'ert-x) 28(require 'ert-x)
29 29
30(ert-deftest test-vstable-compute-columns () 30(defun vtable-tests--make-no-header-2-object-table ()
31 (make-vtable :columns '("a" "b" "c")
32 :objects '(("foo" 1 2)
33 ("bar" 3 :zot))
34 :insert nil))
35
36(ert-deftest test-vtable-compute-columns ()
31 (should 37 (should
32 (equal (mapcar 38 (equal (mapcar
33 (lambda (column) 39 (lambda (column)
34 (vtable-column-align column)) 40 (vtable-column-align column))
35 (vtable--compute-columns 41 (vtable--compute-columns
36 (make-vtable :columns '("a" "b" "c") 42 (vtable-tests--make-no-header-2-object-table)))
37 :objects '(("foo" 1 2)
38 ("bar" 3 :zot))
39 :insert nil)))
40 '(left right left)))) 43 '(left right left))))
41 44
42(ert-deftest test-vtable-insert-object () 45(ert-deftest test-vtable-insert-object ()
@@ -69,4 +72,82 @@
69 (mapcar #'cadr (vtable-objects table)))) 72 (mapcar #'cadr (vtable-objects table))))
70 (number-sequence 0 11)))) 73 (number-sequence 0 11))))
71 74
75(ert-deftest test-vtable-unique-buffer ()
76 (let ((table (vtable-tests--make-no-header-2-object-table)))
77 (with-temp-buffer
78 (vtable-insert table)
79 (with-temp-buffer
80 (should-error (vtable-insert table)))
81 (with-temp-buffer
82 (vtable-set-buffer table (current-buffer))
83 (vtable-insert table)))))
84
85(ert-deftest test-vtable-read-only-buffer ()
86 (let ((table (vtable-tests--make-no-header-2-object-table)))
87 (with-temp-buffer
88 (setq buffer-read-only t)
89 (vtable-insert table))))
90
91(ert-deftest test-vtable-non-current-buffer-insert-object ()
92 (let ((table (vtable-tests--make-no-header-2-object-table))
93 (obj '("baz" 4 5)))
94 (with-temp-buffer
95 (vtable-insert table)
96 (should (= (count-lines (point-min) (point-max)) 2))
97 (with-temp-buffer
98 (vtable-insert-object table obj))
99 (should (= (count-lines (point-min) (point-max)) 3)))))
100
101(ert-deftest test-vtable-non-current-buffer-remove-object ()
102 (let ((table (vtable-tests--make-no-header-2-object-table))
103 (obj '("baz" 4 5)))
104 (with-temp-buffer
105 (vtable-insert table)
106 (vtable-insert-object table obj)
107 (should (= (count-lines (point-min) (point-max)) 3))
108 (with-temp-buffer
109 (vtable-remove-object table obj))
110 (should (= (count-lines (point-min) (point-max)) 2)))))
111
112(ert-deftest test-vtable-non-current-buffer-update-object ()
113 (let ((table (vtable-tests--make-no-header-2-object-table))
114 (obj '("baz" 4 5))
115 (obj-2 '("qux" 6 7)))
116 (with-temp-buffer
117 (vtable-insert table)
118 (vtable-insert-object table obj)
119 (should (= (count-lines (point-min) (point-max)) 3))
120 (let ((line-2 (progn
121 (goto-char (point-min))
122 (forward-line 2)
123 (buffer-substring (point) (point-max)))))
124 (with-temp-buffer
125 (vtable-update-object table obj-2 obj))
126 (let ((line-2-new (progn
127 (goto-char (point-min))
128 (forward-line 2)
129 (buffer-substring (point) (point-max)))))
130 (should (= (count-lines (point-min) (point-max)) 3))
131 (should (not (string= line-2 line-2-new))))))))
132
133(ert-deftest test-vtable--limit-string-with-face-remapped-buffer ()
134 (with-temp-buffer
135 (let ((text (propertize "XXXXX"
136 'face 'variable-pitch)))
137 (face-remap-add-relative 'default :height 1.5)
138 (cond ((< emacs-major-version 31) ; TODO: Remove the pre-31 test, eventually.
139 (let* ((x-width (string-pixel-width (substring text 0 1)))
140 (char-limit 2)
141 (pixel-limit (* char-limit x-width)))
142 (should (eq
143 char-limit
144 (length (vtable--limit-string text pixel-limit))))))
145 (t
146 (let* ((x-width (string-pixel-width (substring text 0 1) (current-buffer)))
147 (char-limit 2)
148 (pixel-limit (* char-limit x-width)))
149 (should (eq
150 char-limit
151 (length (vtable--limit-string text pixel-limit (current-buffer)))))))))))
152
72;;; vtable-tests.el ends here 153;;; vtable-tests.el ends here