diff options
| author | Neil <nyamatongwe@gmail.com> | 2019-11-24 08:04:57 +1100 | 
|---|---|---|
| committer | Neil <nyamatongwe@gmail.com> | 2019-11-24 08:04:57 +1100 | 
| commit | cf034c5958bd83d4a0f71ff79a47e4f76534d5d6 (patch) | |
| tree | 709910a1d33720c95fa732e5cd7772e4befed213 | |
| parent | df312dd97f04aaed14f528f2f822302042d756c5 (diff) | |
| download | scintilla-mirror-cf034c5958bd83d4a0f71ff79a47e4f76534d5d6.tar.gz | |
Bug [#2140]. Move rather than grow selection when insertion at start.
| -rw-r--r-- | doc/ScintillaHistory.html | 4 | ||||
| -rw-r--r-- | src/Selection.cxx | 21 | ||||
| -rw-r--r-- | src/Selection.h | 2 | ||||
| -rw-r--r-- | test/simpleTests.py | 92 | 
4 files changed, 114 insertions, 5 deletions
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 8109018c1..05e03640f 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -567,6 +567,10 @@  	Released 24 October 2019.  	</li>  	<li> +	Move rather than grow selection when insertion at start. +	<a href="https://sourceforge.net/p/scintilla/bugs/2140/">Bug #2140</a>. +	</li> +	<li>  	Allow target to have virtual space.  	Add methods for finding the virtual space at start and end of multiple selections.  	<a href="https://sourceforge.net/p/scintilla/feature-requests/1316/">Feature #1316</a>. diff --git a/src/Selection.cxx b/src/Selection.cxx index 9b08ea001..5667bf234 100644 --- a/src/Selection.cxx +++ b/src/Selection.cxx @@ -23,12 +23,16 @@  using namespace Scintilla; -void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept { +void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept {  	if (insertion) {  		if (position == startChange) { +			// Always consume virtual space  			const Sci::Position virtualLengthRemove = std::min(length, virtualSpace);  			virtualSpace -= virtualLengthRemove; -			position += virtualLengthRemove; +			if (moveForEqual) { +				const Sci::Position lengthAfterVirtualRemove = length - virtualLengthRemove; +				position += lengthAfterVirtualRemove; +			}  		} else if (position > startChange) {  			position += length;  		} @@ -85,8 +89,17 @@ Sci::Position SelectionRange::Length() const noexcept {  }  void SelectionRange::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept { -	caret.MoveForInsertDelete(insertion, startChange, length); -	anchor.MoveForInsertDelete(insertion, startChange, length); +	// For insertions that occur at the start of the selection move both the start +	// and end of the selection to preserve the selected length. +	// The end will automatically move since it is after the insertion, so determine +	// which position is the start and pass this into +	// SelectionPosition::MoveForInsertDelete. +	// There isn't any reason to move an empty selection so don't move it. +	const bool caretStart = caret < anchor; +	const bool anchorStart = anchor < caret; + +	caret.MoveForInsertDelete(insertion, startChange, length, caretStart); +	anchor.MoveForInsertDelete(insertion, startChange, length, anchorStart);  }  bool SelectionRange::Contains(Sci::Position pos) const noexcept { diff --git a/src/Selection.h b/src/Selection.h index 11a6f097a..741b75277 100644 --- a/src/Selection.h +++ b/src/Selection.h @@ -23,7 +23,7 @@ public:  		position = 0;  		virtualSpace = 0;  	} -	void MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept; +	void MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept;  	bool operator ==(const SelectionPosition &other) const noexcept {  		return position == other.position && virtualSpace == other.virtualSpace;  	} diff --git a/test/simpleTests.py b/test/simpleTests.py index f16d4268c..6cacf1045 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -1387,6 +1387,11 @@ class TestMultiSelection(unittest.TestCase):  		# 3 lines of 3 characters  		t = b"xxx\nxxx\nxxx"  		self.ed.AddText(len(t), t) +		 +	def textOfSelection(self, n): +		self.ed.TargetStart = self.ed.GetSelectionNStart(n) +		self.ed.TargetEnd = self.ed.GetSelectionNEnd(n) +		return bytes(self.ed.GetTargetText())  	def testSelectionCleared(self):  		self.ed.ClearSelections() @@ -1542,6 +1547,93 @@ class TestMultiSelection(unittest.TestCase):  		self.ed.DropSelectionN(0)  		self.assertEquals(self.ed.MainSelection, 2) +	def partFromSelection(self, n): +		# Return a tuple (order, text) from a selection part +		# order is a boolean whether the caret is before the anchor +		self.ed.TargetStart = self.ed.GetSelectionNStart(n) +		self.ed.TargetEnd = self.ed.GetSelectionNEnd(n) +		return (self.ed.GetSelectionNCaret(n) < self.ed.GetSelectionNAnchor(n), self.textOfSelection(n)) + +	def replacePart(self, n, part): +		startSelection = self.ed.GetSelectionNStart(n) +		self.ed.TargetStart = startSelection +		self.ed.TargetEnd = self.ed.GetSelectionNEnd(n) +		direction, text = part +		length = len(text) +		self.ed.ReplaceTarget(len(text), text) +		if direction: +			self.ed.SetSelectionNCaret(n, startSelection) +			self.ed.SetSelectionNAnchor(n, startSelection + length) +		else: +			self.ed.SetSelectionNAnchor(n, startSelection) +			self.ed.SetSelectionNCaret(n, startSelection + length) + +	def swapSelections(self): +		# Swap the selections +		part0 = self.partFromSelection(0) +		part1 = self.partFromSelection(1) + +		self.replacePart(1, part0) +		self.replacePart(0, part1) + +	def checkAdjacentSelections(self, selections, invert): +		# Only called from testAdjacentSelections to try one permutation +		self.ed.ClearAll() +		self.ed.EmptyUndoBuffer() +		t = b"ab" +		texts = (b'b', b'a') if invert else (b'a', b'b') +		self.ed.AddText(len(t), t) +		sel0, sel1 = selections +		self.ed.SetSelection(sel0[0], sel0[1]) +		self.ed.AddSelection(sel1[0], sel1[1]) +		self.assertEquals(self.ed.Selections, 2) +		self.assertEquals(self.textOfSelection(0), texts[0]) +		self.assertEquals(self.textOfSelection(1), texts[1]) +		self.swapSelections() +		self.assertEquals(self.ed.Contents(), b'ba') +		self.assertEquals(self.ed.Selections, 2) +		self.assertEquals(self.textOfSelection(0), texts[1]) +		self.assertEquals(self.textOfSelection(1), texts[0]) + +	def testAdjacentSelections(self): +		# For various permutations of selections, try swapping the text and ensure that the +		# selections remain distinct +		self.checkAdjacentSelections(((1, 0),(1, 2)), False) +		self.checkAdjacentSelections(((0, 1),(1, 2)), False) +		self.checkAdjacentSelections(((1, 0),(2, 1)), False) +		self.checkAdjacentSelections(((0, 1),(2, 1)), False) + +		# Reverse order, first selection is after second +		self.checkAdjacentSelections(((1, 2),(1, 0)), True) +		self.checkAdjacentSelections(((1, 2),(0, 1)), True) +		self.checkAdjacentSelections(((2, 1),(1, 0)), True) +		self.checkAdjacentSelections(((2, 1),(0, 1)), True) + +	def testInsertBefore(self): +		self.ed.ClearAll() +		t = b"a" +		self.ed.AddText(len(t), t) +		self.ed.SetSelection(0, 1) +		self.assertEquals(self.textOfSelection(0), b'a') + +		self.ed.SetTargetRange(0, 0) +		self.ed.ReplaceTarget(1, b'1') +		self.assertEquals(self.ed.Contents(), b'1a') +		self.assertEquals(self.textOfSelection(0), b'a') + +	def testInsertAfter(self): +		self.ed.ClearAll() +		t = b"a" +		self.ed.AddText(len(t), t) +		self.ed.SetSelection(0, 1) +		self.assertEquals(self.textOfSelection(0), b'a') + +		self.ed.SetTargetRange(1, 1) +		self.ed.ReplaceTarget(1, b'9') +		self.assertEquals(self.ed.Contents(), b'a9') +		self.assertEquals(self.textOfSelection(0), b'a') + +  class TestModalSelection(unittest.TestCase):  	def setUp(self):  | 
