From f54fd2019dd648b29a80ec7429833ab546d3a428 Mon Sep 17 00:00:00 2001 From: Neil Date: Sat, 1 Feb 2025 14:43:22 +1100 Subject: Serialize selection type and ranges with SCI_GETSELECTIONSERIALIZED and SCI_SETSELECTIONSERIALIZED. --- call/ScintillaCall.cxx | 12 ++++++++++++ doc/ScintillaDoc.html | 12 +++++++++++- doc/ScintillaHistory.html | 3 +++ include/Scintilla.h | 2 ++ include/Scintilla.iface | 6 ++++++ include/ScintillaCall.h | 3 +++ include/ScintillaMessages.h | 2 ++ src/Editor.cxx | 18 ++++++++++++++++++ src/Editor.h | 1 + src/Selection.cxx | 17 +++++++++++++++++ src/Selection.h | 2 ++ test/simpleTests.py | 33 +++++++++++++++++++++++++++++++++ 12 files changed, 110 insertions(+), 1 deletion(-) diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx index 550adc710..57b050805 100644 --- a/call/ScintillaCall.cxx +++ b/call/ScintillaCall.cxx @@ -1267,6 +1267,18 @@ UndoSelectionHistoryOption ScintillaCall::UndoSelectionHistory() { return static_cast(Call(Message::GetUndoSelectionHistory)); } +void ScintillaCall::SetSelectionSerialized(const char *selectionString) { + CallString(Message::SetSelectionSerialized, 0, selectionString); +} + +Position ScintillaCall::SelectionSerialized(char *selectionString) { + return CallPointer(Message::GetSelectionSerialized, 0, selectionString); +} + +std::string ScintillaCall::SelectionSerialized() { + return CallReturnString(Message::GetSelectionSerialized, 0); +} + Line ScintillaCall::FirstVisibleLine() { return Call(Message::GetFirstVisibleLine); } diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 886786484..dda900b7c 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -130,7 +130,7 @@

Scintilla Documentation

-

Last edited 26 October 2024 NH

+

Last edited 1 February 2025 NH

Scintilla 5 has moved the lexers from Scintilla into a new Lexilla project.
@@ -1239,6 +1239,10 @@ struct Sci_TextRangeFull { SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE → position

+ SCI_SETSELECTIONSERIALIZED(<unused>, const char *selectionString)
+ SCI_GETSELECTIONSERIALIZED(<unused>, char *selectionString) → position
+
+ SC_ELEMENT_SELECTION_ADDITIONAL_TEXT : colouralpha
SC_ELEMENT_SELECTION_ADDITIONAL_BACK : colouralpha
SCI_SETADDITIONALSELALPHA(alpha alpha)
@@ -1404,6 +1408,12 @@ struct Sci_TextRangeFull { Set or query the position and amount of virtual space for the caret and anchor of the rectangular selection. After setting the rectangular selection, this is broken down into multiple selections, one for each line.

+

+ SCI_SETSELECTIONSERIALIZED(<unused>, const char *selectionString)
+ SCI_GETSELECTIONSERIALIZED(<unused>, char *selectionString) → position
+ Set or query the selection type and positions as a serialized string. + The format of this string may change in future versions so should not be persisted beyond the current session.

+

SC_ELEMENT_SELECTION_ADDITIONAL_TEXT : colouralpha
SC_ELEMENT_SELECTION_ADDITIONAL_BACK : colouralpha
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 7cb5ebcee..782801b7a 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -601,6 +601,9 @@ Bug #1224.

  • + Serialize selection type and ranges with SCI_GETSELECTIONSERIALIZED and SCI_SETSELECTIONSERIALIZED. +
  • +
  • Fix bug on Qt where double-click stopped working when Scintilla instance had been running for weeks.
  • diff --git a/include/Scintilla.h b/include/Scintilla.h index 736a7e2d5..4de92e1b8 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -536,6 +536,8 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SC_UNDO_SELECTION_HISTORY_ENABLED 1 #define SCI_SETUNDOSELECTIONHISTORY 2782 #define SCI_GETUNDOSELECTIONHISTORY 2783 +#define SCI_SETSELECTIONSERIALIZED 2784 +#define SCI_GETSELECTIONSERIALIZED 2785 #define SCI_GETFIRSTVISIBLELINE 2152 #define SCI_GETLINE 2153 #define SCI_GETLINECOUNT 2154 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 65d3d157b..4ce9d5703 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -1344,6 +1344,12 @@ set void SetUndoSelectionHistory=2782(UndoSelectionHistoryOption undoSelectionHi # Report undo selection history status. get UndoSelectionHistoryOption GetUndoSelectionHistory=2783(,) +# Set selection from serialized form. +set void SetSelectionSerialized=2784(, string selectionString) + +# Retrieve serialized form of selection. +get position GetSelectionSerialized=2785(, stringresult selectionString) + # Retrieve the display line at the top of the display. get line GetFirstVisibleLine=2152(,) diff --git a/include/ScintillaCall.h b/include/ScintillaCall.h index bcd462be9..345d15612 100644 --- a/include/ScintillaCall.h +++ b/include/ScintillaCall.h @@ -362,6 +362,9 @@ public: Scintilla::ChangeHistoryOption ChangeHistory(); void SetUndoSelectionHistory(Scintilla::UndoSelectionHistoryOption undoSelectionHistory); Scintilla::UndoSelectionHistoryOption UndoSelectionHistory(); + void SetSelectionSerialized(const char *selectionString); + Position SelectionSerialized(char *selectionString); + std::string SelectionSerialized(); Line FirstVisibleLine(); Position GetLine(Line line, char *text); std::string GetLine(Line line); diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h index f60be4d52..d7ec27c39 100644 --- a/include/ScintillaMessages.h +++ b/include/ScintillaMessages.h @@ -287,6 +287,8 @@ enum class Message { GetChangeHistory = 2781, SetUndoSelectionHistory = 2782, GetUndoSelectionHistory = 2783, + SetSelectionSerialized = 2784, + GetSelectionSerialized = 2785, GetFirstVisibleLine = 2152, GetLine = 2153, GetLineCount = 2154, diff --git a/src/Editor.cxx b/src/Editor.cxx index 10c694990..d85a4c294 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -711,6 +711,15 @@ void Editor::SetEmptySelection(Sci::Position currentPos_) { SetEmptySelection(SelectionPosition(currentPos_)); } +void Editor::SetSelectionFromSerialized(const char *serialized) { + if (serialized) { + sel = Selection(serialized); + sel.Truncate(pdoc->Length()); + SetRectangularRange(); + InvalidateStyleRedraw(); + } +} + void Editor::MultipleSelectAdd(AddNumber addNumber) { if (SelectionEmpty() || !multipleSelection) { // Select word at caret @@ -8695,6 +8704,15 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::GetUndoSelectionHistory: return static_cast(undoSelectionHistoryOption); + case Message::SetSelectionSerialized: + SetSelectionFromSerialized(ConstCharPtrFromSPtr(lParam)); + break; + + case Message::GetSelectionSerialized: { + const std::string serialized = sel.ToString(); + return BytesResult(lParam, serialized); + } + case Message::SetExtraAscent: vs.extraAscent = static_cast(wParam); InvalidateStyleRedraw(); diff --git a/src/Editor.h b/src/Editor.h index a6a484ede..ff940bc49 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -352,6 +352,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void SetSelection(SelectionPosition currentPos_); void SetEmptySelection(SelectionPosition currentPos_); void SetEmptySelection(Sci::Position currentPos_); + void SetSelectionFromSerialized(const char *serialized); enum class AddNumber { one, each }; void MultipleSelectAdd(AddNumber addNumber); bool RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept; diff --git a/src/Selection.cxx b/src/Selection.cxx index 5650d5c7e..7b517cc4a 100644 --- a/src/Selection.cxx +++ b/src/Selection.cxx @@ -225,6 +225,13 @@ bool SelectionRange::Trim(SelectionRange range) noexcept { } } +void SelectionRange::Truncate(Sci::Position length) noexcept { + if (anchor.Position() > length) + anchor.SetPosition(length); + if (caret.Position() > length) + caret.SetPosition(length); +} + // If range is all virtual collapse to start of virtual space void SelectionRange::MinimizeVirtualSpace() noexcept { if (caret.Position() == anchor.Position()) { @@ -568,6 +575,16 @@ void Selection::SetRanges(const Ranges &rangesToSet) { ranges = rangesToSet; } +void Selection::Truncate(Sci::Position length) noexcept { + // This may be needed when applying a persisted selection onto a document that has been shortened. + for (SelectionRange &range : ranges) { + range.Truncate(length); + } + // Above may have made some non-unique empty ranges. + RemoveDuplicates(); + rangeRectangular.Truncate(length); +} + std::string Selection::ToString() const { std::string result; switch (selType) { diff --git a/src/Selection.h b/src/Selection.h index 68e7a4e54..57de92a57 100644 --- a/src/Selection.h +++ b/src/Selection.h @@ -148,6 +148,7 @@ struct SelectionRange { } void Swap() noexcept; bool Trim(SelectionRange range) noexcept; + void Truncate(Sci::Position length) noexcept; // If range is all virtual collapse to start of virtual space void MinimizeVirtualSpace() noexcept; std::string ToString() const; @@ -216,6 +217,7 @@ public: return ranges; } void SetRanges(const Ranges &rangesToSet); + void Truncate(Sci::Position length) noexcept; std::string ToString() const; }; diff --git a/test/simpleTests.py b/test/simpleTests.py index ce67edfde..867c0ebbc 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -2242,6 +2242,39 @@ class TestMultiSelection(unittest.TestCase): self.assertEqual(self.ed.Contents(), b'a 1') self.assertEqual(self.textOfSelection(0), b' ') + def testSelectionSerialization(self): + self.ed.SetContents(b"a") + self.ed.SetSelection(0, 1) + self.assertEqual(self.ed.GetSelectionSerialized(), b'1-0') + self.ed.SetSelection(1, 1) + self.assertEqual(self.ed.GetSelectionSerialized(), b'1') + self.ed.SetSelectionNAnchorVirtualSpace(0, 2) + self.ed.SetSelectionNCaretVirtualSpace(0, 3) + self.assertEqual(selectionRepresentation(self.ed, 0), "1+2v-1+3v") + self.assertEqual(self.textOfSelection(0), b'') + self.assertEqual(self.ed.GetSelectionSerialized(), b'1v2-1v3') + self.ed.SetSelectionSerialized(0, b'1-0') + self.assertEqual(self.ed.MainSelection, 0) + self.assertEqual(self.ed.Anchor, 1) + self.assertEqual(self.ed.CurrentPos, 0) + self.assertEqual(self.ed.GetSelectionNAnchor(0), 1) + self.assertEqual(self.ed.GetSelectionNCaret(0), 0) + + def testSelectionSerializationOutOfBounds(self): + # Try setting selections that extend past document end through serialized form + # and check that the selection is limited to the document. + self.ed.SetContents(b"a") + self.ed.SetSelectionSerialized(0, b'200-0') + self.assertEqual(self.ed.GetSelectionSerialized(), b'1-0') + + # Retain virtual space + self.ed.SetSelectionSerialized(0, b'0v1-200') + self.assertEqual(self.ed.GetSelectionSerialized(), b'0v1-1') + + # Drop identical ranges, but touching empty range survives + self.ed.SetSelectionSerialized(0, b'0-200,300-400,500-600') + self.assertEqual(self.ed.GetSelectionSerialized(), b'0-1,1') + class TestModalSelection(unittest.TestCase): -- cgit v1.2.3