I've been working on a patch for LFUN_PARAGRAPH_MOVE_DOWN move subitems
of an item along with the item. For example, consider the following
situation:

  1. One item <cur>
     a. "subitem"
  2. Another item

Currently (i.e., without the patch), if the cursor is at positiion <cur>
and the user executes a paragraph-move-down, we get the following:

  1. "subitem"
  2. One item <cur>
  3. Another item

With the patch, the user would get the following:

  1. Another item
  2. One item <cur>
     a. "subitem"

I believe this functionality would be consistent with the current
behavior of outline-down (which has the same keybinding as
paragraph-move-down), e.g., subsections are moved with sections.

Does anyone have comments on the patch, either the desired functionality
or the actual code?

Can you think of any strange cases that I can test this code on?

If no one is against this approach, I will continue working on it and
commit. I still need to do the following:

  - Update the documentation of the LFUN and release notes.

  - Implement consistent behavior for paragraph-move-up.

  - Factor out code into methods (this will make it easy to
    implement paragraph-move-up).

  - The algorithm for swapping the paragraphs in this patch is
    inefficient (i.e., the end result could be achieved with fewer
    swaps). I can work on a more efficient algorithm.

Scott

From ea70a6c3fe273c9c6ed9cedc7bebf6cc4d105d52 Mon Sep 17 00:00:00 2001
From: Scott Kostyshak <skost...@lyx.org>
Date: Fri, 31 Jan 2020 13:35:16 -0500
Subject: [PATCH] paragraph-move-down now moves sub-items as well

---
 src/Text3.cpp                  | 50 ++++++++++++++++++++++++++++++----
 src/support/RandomAccessList.h | 20 ++++++++++++++
 2 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/src/Text3.cpp b/src/Text3.cpp
index 8643411da1..9a1470aea3 100644
--- a/src/Text3.cpp
+++ b/src/Text3.cpp
@@ -671,13 +671,53 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 	switch (act) {
 
 	case LFUN_PARAGRAPH_MOVE_DOWN: {
-		pit_type const pit = cur.pit();
-		cur.recordUndo(pit, pit + 1);
-		cur.finishUndo();
-		pars_.swap(pit, pit + 1);
+		depth_type const cur_depth = pars_[cur.pit()].params().depth();
+		pit_type pit = cur.pit() + 1;
+		Paragraph cpar;
+		while (pit != pit_type(pars_.size())) {
+			cpar = pars_[pit];
+			// <= because it's possible to go e.g. from depth 2 to 0
+			if (cpar.params().depth() <= cur_depth)
+				break;
+			++pit;
+		}
+		if (cpar.params().depth() < cur_depth) {
+			// we did not encounter an equal depth before a smaller
+			// depth, so there is no sibling. e.g.:
+			// 1. hello
+			//    a. blah <cur>
+			// 2. goodbye
+			break;
+		}
+		if (cpar.params().depth() > cur_depth) {
+			// all paragraphs below cur have larger depth
+			// so there is no next sibling. e.g.:
+			// 1. hello <cur>
+			//    a. blah
+			// <end of document>
+			break;
+		}
+		pit_type const next_sib = pit;
+
+		// this is the depth of the paragraph below whose "family" we
+		// want to move.
+		depth_type next_sib_depth = cpar.params().depth();
+		pit_type pit2 = next_sib + 1;
+		while (pit2 != pit_type(pars_.size())) {
+			cpar = pars_[pit2];
+			// <= because it's possible to go e.g. from depth 2 to 0
+			if (cpar.params().depth() <= next_sib_depth)
+				break;
+			++pit2;
+		}
+		pit_type const next_sib_end = pit2 - 1;
+
+		cur.recordUndo(cur.pit(), next_sib_end);
+		pars_.swap(cur.pit(), next_sib - 1, next_sib_end);
 		needsUpdate = true;
 		cur.forceBufferUpdate();
-		++cur.pit();
+		// move the cursor with the paragraph
+		cur.pit() = next_sib_end - (next_sib - cur.pit()) + 1;
 		break;
 	}
 
diff --git a/src/support/RandomAccessList.h b/src/support/RandomAccessList.h
index 2565d62513..8a7eda0ca4 100644
--- a/src/support/RandomAccessList.h
+++ b/src/support/RandomAccessList.h
@@ -246,6 +246,26 @@ public:
 		return it;
 	}
 
+	void swap(size_t from_beg, size_t from_end, size_t to_end)
+	{
+		// swap of contiguous ranges of elements:
+		//
+		// swap elements from_beg through from_end with
+		// elements from_end + 1 through to_end.
+		// The ranges do not need to be the same length.
+
+		// number of swaps for each element in "to" range
+		int const num_swaps = (from_end + 1) - from_beg;
+
+		// swap each element in "to" range up to the correct
+		// location, one-by-one (i.e., inefficient)
+		for (size_t i = from_end + 1; i != to_end + 1; i++) {
+			for (size_t j = i; j != i - num_swaps; j--) {
+				swap(j, j - 1);
+			}
+		}
+	}
+
 	void swap(size_t i, size_t j)
 	{
 		size_t const p = std::max(i, j);
-- 
2.20.1

Attachment: signature.asc
Description: PGP signature

-- 
lyx-devel mailing list
lyx-devel@lists.lyx.org
http://lists.lyx.org/mailman/listinfo/lyx-devel

Reply via email to