summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Kauffman <kauffman@ecs.gmu.edu>2017-07-23 00:13:11 -0400
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2018-10-03 18:44:34 +0200
commitd0a5308435d35c21d24965383970cd70d61f5886 (patch)
tree2af96eb6bdf02717e8e0496617aada9c0d6c82f2
parent74f769f85045bd37bbe4cdd953613cf4a38de673 (diff)
downloadorg-mode-d0a5308435d35c21d24965383970cd70d61f5886.tar.gz
org-table: Adding single cell movement functions
* lisp/org-table.el (org-table--swap-cells): (org-table--move-cell): (org-table-move-cell-up): (org-table-move-cell-down): (org-table-move-cell-left): (org-table-move-cell-right): New functions. * testing/lisp/test-org-table.el (test-org-table/move-cell-down): (test-org-table/move-cell-up): (test-org-table/move-cell-right): (test-org-table/move-cell-left): New tests. * doc/org-manual.org (Column and row editing): Document functions and keybindings for single cell movement.
-rw-r--r--doc/org-manual.org24
-rw-r--r--lisp/org-table.el74
-rw-r--r--testing/lisp/test-org-table.el385
3 files changed, 483 insertions, 0 deletions
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 535aa9d..9db0092 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -1556,6 +1556,30 @@ you, configure the option ~org-table-auto-blank-field~.
#+findex: org-table-kill-row
Kill the current row or horizontal line.
+- {{{kbd(S-UP)}}} (~org-table-move-cell-up~) ::
+
+ #+kindex: S-UP
+ #+findex: org-table-move-cell-up
+ Move cell up by swapping with adjacent cell.
+
+- {{{kbd(S-DOWN)}}} (~org-table-move-cell-down~) ::
+
+ #+kindex: S-DOWN
+ #+findex: org-table-move-cell-down
+ Move cell down by swapping with adjacent cell.
+
+- {{{kbd(S-LEFT)}}} (~org-table-move-cell-left~) ::
+
+ #+kindex: S-LEFT
+ #+findex: org-table-move-cell-left
+ Move cell left by swapping with adjacent cell.
+
+- {{{kbd(S-RIGHT)}}} (~org-table-move-cell-right~) ::
+
+ #+kindex: S-RIGHT
+ #+findex: org-table-move-cell-right
+ Move cell right by swapping with adjacent cell.
+
- {{{kbd(M-S-DOWN)}}} (~org-table-insert-row~) ::
#+kindex: M-S-DOWN
diff --git a/lisp/org-table.el b/lisp/org-table.el
index 8eb38ef..634dd14 100644
--- a/lisp/org-table.el
+++ b/lisp/org-table.el
@@ -1442,6 +1442,80 @@ non-nil, the one above is used."
(above min)
(t max)))))))
+(defun org-table--swap-cells (row1 col1 row2 col2)
+ "Swap two cells indicated by the coordinates provided.
+ROW1, COL1, ROW2, COL2 are integers indicating the row/column
+position of the two cells that will be swapped in the table."
+ (let ((content1 (org-table-get row1 col1))
+ (content2 (org-table-get row2 col2)))
+ (org-table-put row1 col1 content2)
+ (org-table-put row2 col2 content1)))
+
+(defun org-table--move-cell (direction)
+ "Move the current cell in a cardinal direction.
+DIRECTION is a symbol among `up', `down', `left', and `right'.
+The contents the current cell are swapped with cell in the
+indicated direction. Raise an error if the move cannot be done."
+ (let ((row-shift (pcase direction (`up -1) (`down 1) (_ 0)))
+ (column-shift (pcase direction (`left -1) (`right 1) (_ 0))))
+ (when (and (= 0 row-shift) (= 0 column-shift))
+ (error "Invalid direction: %S" direction))
+ ;; Initialize `org-table-current-ncol' and `org-table-dlines'.
+ (org-table-analyze)
+ (let* ((row (org-table-current-line))
+ (column (org-table-current-column))
+ (target-row (+ row row-shift))
+ (target-column (+ column column-shift))
+ (org-table-current-nrow (1- (length org-table-dlines))))
+ (when (or (< target-column 1)
+ (< target-row 1)
+ (> target-column org-table-current-ncol)
+ (> target-row org-table-current-nrow))
+ (user-error "Cannot move cell further"))
+ (org-table--swap-cells row column target-row target-column)
+ (org-table-goto-line target-row)
+ (org-table-goto-column target-column))))
+
+;;;###autoload
+(defun org-table-move-cell-up ()
+ "Move a single cell up in a table.
+Swap with anything in target cell."
+ (interactive)
+ (unless (org-table-check-inside-data-field)
+ (error "No table at point"))
+ (org-table--move-cell 'up)
+ (org-table-align))
+
+;;;###autoload
+(defun org-table-move-cell-down ()
+ "Move a single cell down in a table.
+Swap with anything in target cell."
+ (interactive)
+ (unless (org-table-check-inside-data-field)
+ (error "No table at point"))
+ (org-table--move-cell 'down)
+ (org-table-align))
+
+;;;###autoload
+(defun org-table-move-cell-left ()
+ "Move a single cell left in a table.
+Swap with anything in target cell."
+ (interactive)
+ (unless (org-table-check-inside-data-field)
+ (error "No table at point"))
+ (org-table--move-cell 'left)
+ (org-table-align))
+
+;;;###autoload
+(defun org-table-move-cell-right ()
+ "Move a single cell right in a table.
+Swap with anything in target cell."
+ (interactive)
+ (unless (org-table-check-inside-data-field)
+ (error "No table at point"))
+ (org-table--move-cell 'right)
+ (org-table-align))
+
;;;###autoload
(defun org-table-delete-column ()
"Delete a column from the table."
diff --git a/testing/lisp/test-org-table.el b/testing/lisp/test-org-table.el
index ecef7ea..7af5c9f 100644
--- a/testing/lisp/test-org-table.el
+++ b/testing/lisp/test-org-table.el
@@ -2278,6 +2278,391 @@ See also `test-org-table/copy-field'."
+;;; Moving single cells
+(ert-deftest test-org-table/move-cell-down ()
+ "Test `org-table-move-cell-down' specifications."
+ ;; Error out when cell cannot be moved due to not in table, in the
+ ;; last row of the table, or is on a hline.
+ (should-error
+ (org-test-with-temp-text "not in\na table\n"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "| a |"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "| a |\n"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "| a | <point>b |\n"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "| a | b |\n| <point>c | d |\n"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "| a | b |\n| c | <point>d |\n"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "| <point>a |\n|---|\n"
+ (org-table-move-cell-down)))
+ (should-error
+ (org-test-with-temp-text "|<point>---|\n| a |\n"
+ (org-table-move-cell-down)))
+ ;; Check for correct cell movement
+ (should (equal (concat "| c | b |\n"
+ "| a | d |\n"
+ "| e | f |\n")
+ (org-test-with-temp-text
+ (concat "| <point>a | b |\n"
+ "| c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (buffer-string))))
+ (should (equal (concat "| a | d |\n"
+ "| c | b |\n"
+ "| e | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | <point>b |\n"
+ "| c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (buffer-string))))
+ (should (equal (concat "| a | b |\n"
+ "| e | d |\n"
+ "| c | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "| <point>c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (buffer-string))))
+ (should (equal (concat "| a | d |\n"
+ "| c | f |\n"
+ "| e | b |\n")
+ (org-test-with-temp-text
+ (concat "| a |<point> b |\n"
+ "| c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (org-table-move-cell-down)
+ (buffer-string))))
+ ;; Check for correct handling of hlines which should not change
+ ;; position on single cell moves.
+ (should (equal (concat "| c | b |\n"
+ "|---+---|\n"
+ "| a | d |\n"
+ "| e | f |\n")
+ (org-test-with-temp-text
+ (concat "| <point>a | b |\n"
+ "|---+---|\n"
+ "| c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (buffer-string))))
+ (should (equal (concat "| a | d |\n"
+ "|---+---|\n"
+ "| c | f |\n"
+ "| e | b |\n")
+ (org-test-with-temp-text
+ (concat "| a | <point>b |\n"
+ "|---+---|\n"
+ "| c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (org-table-move-cell-down)
+ (buffer-string))))
+ (should (equal (concat "| a | b |\n"
+ "|---+---|\n"
+ "| c | f |\n"
+ "| e | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "|---+---|\n"
+ "| c | <point>d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-down)
+ (buffer-string))))
+ ;; Move single cell even without a final newline.
+ (should (equal (concat "| a | d |\n"
+ "|---+---|\n"
+ "| c | f |\n"
+ "| e | b |\n")
+ (org-test-with-temp-text
+ (concat "| a | <point>b |\n"
+ "|---+---|\n"
+ "| c | d |\n"
+ "| e | f |")
+ (org-table-move-cell-down)
+ (org-table-move-cell-down)
+ (buffer-string)))))
+
+(ert-deftest test-org-table/move-cell-up ()
+ "Test `org-table-move-cell-up' specifications."
+ ;; Error out when cell cannot be moved due to not in table, in the
+ ;; last row of the table, or is on a hline.
+ (should-error
+ (org-test-with-temp-text "not in\na table\n"
+ (org-table-move-cell-up)))
+ (should-error
+ (org-test-with-temp-text "| a |"
+ (org-table-move-cell-up)))
+ (should-error
+ (org-test-with-temp-text "| a |\n"
+ (org-table-move-cell-up)))
+ (should-error
+ (org-test-with-temp-text "| <point>a | b |\n"
+ (org-table-move-cell-up)))
+ (should-error
+ (org-test-with-temp-text "| a | <point>b |\n| c | d |\n"
+ (org-table-move-cell-up)))
+ (should-error
+ (org-test-with-temp-text "| <point>a |\n|---|\n"
+ (org-table-move-cell-up)))
+ (should-error
+ (org-test-with-temp-text "|<point>---|\n| a |\n"
+ (org-table-move-cell-up)))
+ ;; Check for correct cell movement.
+ (should (equal (concat "| c | b |\n"
+ "| a | d |\n"
+ "| e | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "| <point>c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-up)
+ (buffer-string))))
+ (should (equal (concat "| a | d |\n"
+ "| c | b |\n"
+ "| e | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "| c | <point>d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-up)
+ (buffer-string))))
+ (should (equal (concat "| a | b |\n"
+ "| e | d |\n"
+ "| c | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "| c | d |\n"
+ "| <point>e | f |\n")
+ (org-table-move-cell-up)
+ (buffer-string))))
+ (should (equal (concat "| a | f |\n"
+ "| c | b |\n"
+ "| e | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "| c | d |\n"
+ "| e |<point> f |\n")
+ (org-table-move-cell-up)
+ (org-table-move-cell-up)
+ (buffer-string))))
+ ;; Check for correct handling of hlines which should not change
+ ;; position on single cell moves.
+ (should (equal (concat "| c | b |\n"
+ "|---+---|\n"
+ "| a | d |\n"
+ "| e | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "|---+---|\n"
+ "| <point>c | d |\n"
+ "| e | f |\n")
+ (org-table-move-cell-up)
+ (buffer-string))))
+ (should (equal (concat "| a | f |\n"
+ "|---+---|\n"
+ "| c | b |\n"
+ "| e | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "|---+---|\n"
+ "| c | d |\n"
+ "| e | <point>f |\n")
+ (org-table-move-cell-up)
+ (org-table-move-cell-up)
+ (buffer-string))))
+ (should (equal (concat "| a | b |\n"
+ "|---+---|\n"
+ "| c | f |\n"
+ "| e | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "|---+---|\n"
+ "| c | d |\n"
+ "| e | <point>f |\n")
+ (org-table-move-cell-up)
+ (buffer-string))))
+ ;; Move single cell even without a final newline.
+ (should (equal (concat "| a | f |\n"
+ "|---+---|\n"
+ "| c | b |\n"
+ "| e | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b |\n"
+ "|---+---|\n"
+ "| c | d |\n"
+ "| e | <point>f |")
+ (org-table-move-cell-up)
+ (org-table-move-cell-up)
+ (buffer-string)))))
+
+(ert-deftest test-org-table/move-cell-right ()
+ "Test `org-table-move-cell-right' specifications."
+ ;; Error out when cell cannot be moved due to not in table, in the
+ ;; last col of the table, or is on a hline.
+ (should-error
+ (org-test-with-temp-text "not in\na table\n"
+ (org-table-move-cell-right)))
+ (should-error
+ (org-test-with-temp-text "| a |"
+ (org-table-move-cell-right)))
+ (should-error
+ (org-test-with-temp-text "| a |\n"
+ (org-table-move-cell-right)))
+ (should-error
+ (org-test-with-temp-text "| <point>a |\n| b |\n"
+ (org-table-move-cell-right)))
+ (should-error
+ (org-test-with-temp-text "| a | <point>b |\n| c | d |\n"
+ (org-table-move-cell-right)))
+ (should-error
+ (org-test-with-temp-text "| <point>a |\n|---|\n"
+ (org-table-move-cell-right)))
+ (should-error
+ (org-test-with-temp-text "|<point>---|\n| a |\n"
+ (org-table-move-cell-right)))
+ ;; Check for correct cell movement.
+ (should (equal (concat "| b | a | c |\n"
+ "| d | e | f |\n")
+ (org-test-with-temp-text
+ (concat "| <point>a | b | c |\n"
+ "| d | e | f |\n")
+ (org-table-move-cell-right)
+ (buffer-string))))
+ (should (equal (concat "| b | c | a |\n"
+ "| d | e | f |\n")
+ (org-test-with-temp-text
+ (concat "| <point>a | b | c |\n"
+ "| d | e | f |\n")
+ (org-table-move-cell-right)
+ (org-table-move-cell-right)
+ (buffer-string))))
+ (should (equal (concat "| a | b | c |\n"
+ "| e | f | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "| <point> d | e | f |\n")
+ (org-table-move-cell-right)
+ (org-table-move-cell-right)
+ (buffer-string))))
+ (should (equal (concat "| a | b | c |\n"
+ "| d | f | e |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "| d | <point>e | f |\n")
+ (org-table-move-cell-right)
+ (buffer-string))))
+ (should (equal (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| e | f | d |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| <point>d | e | f |\n")
+ (org-table-move-cell-right)
+ (org-table-move-cell-right)
+ (buffer-string))))
+ ;; Move single cell even without a final newline.
+ (should (equal (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| e | d | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| <point>d | e | f |")
+ (org-table-move-cell-right)
+ (buffer-string)))))
+
+(ert-deftest test-org-table/move-cell-left ()
+ "Test `org-table-move-cell-left' specifications."
+ ;; Error out when cell cannot be moved due to not in table, in the
+ ;; last col of the table, or is on a hline.
+ (should-error
+ (org-test-with-temp-text "not in\na table\n"
+ (org-table-move-cell-left)))
+ (should-error
+ (org-test-with-temp-text "| a |"
+ (org-table-move-cell-left)))
+ (should-error
+ (org-test-with-temp-text "| a |\n"
+ (org-table-move-cell-left)))
+ (should-error
+ (org-test-with-temp-text "| <point>a |\n| b |\n"
+ (org-table-move-cell-left)))
+ (should-error
+ (org-test-with-temp-text "| <point>a | b |\n| c | d |\n"
+ (org-table-move-cell-left)))
+ (should-error
+ (org-test-with-temp-text "| <point>a |\n|---|\n"
+ (org-table-move-cell-left)))
+ (should-error
+ (org-test-with-temp-text "|<point>---|\n| a |\n"
+ (org-table-move-cell-left)))
+ ;; Check for correct cell movement.
+ (should (equal (concat "| b | a | c |\n"
+ "| d | e | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | <point>b | c |\n"
+ "| d | e | f |\n")
+ (org-table-move-cell-left)
+ (buffer-string))))
+ (should (equal (concat "| c | a | b |\n"
+ "| d | e | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | <point>c |\n"
+ "| d | e | f |\n")
+ (org-table-move-cell-left)
+ (org-table-move-cell-left)
+ (buffer-string))))
+ (should (equal (concat "| a | b | c |\n"
+ "| f | d | e |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "| d | e | <point>f |\n")
+ (org-table-move-cell-left)
+ (org-table-move-cell-left)
+ (buffer-string))))
+ (should (equal (concat "| a | b | c |\n"
+ "| d | f | e |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "| d | e | <point>f |\n")
+ (org-table-move-cell-left)
+ (buffer-string))))
+ (should (equal (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| f | d | e |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| d | e | <point>f |\n")
+ (org-table-move-cell-left)
+ (org-table-move-cell-left)
+ (buffer-string))))
+ ;; Move single cell even without a final newline.
+ (should (equal (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| e | d | f |\n")
+ (org-test-with-temp-text
+ (concat "| a | b | c |\n"
+ "|---+---+---|\n"
+ "| d | <point>e | f |")
+ (org-table-move-cell-left)
+ (buffer-string)))))
+
+
;;; Moving rows, moving columns
(ert-deftest test-org-table/move-row-down ()