aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorZufu Liu <unknown>2025-07-03 08:23:26 +1000
committerZufu Liu <unknown>2025-07-03 08:23:26 +1000
commitcde12528a7e587833926354b88ad1974aedaf103 (patch)
treea6b75dc168997106346dafdd8a424d44949c29f1
parent181b13c2eed7499d192d0b85c7269dc7ab71336b (diff)
downloadscintilla-mirror-cde12528a7e587833926354b88ad1974aedaf103.tar.gz
Feature [feature-requests:#1563]. Move main range in selection serialized form.master
Avoid processing characters multiple times by relying on from_chars munching digit characters instead of searching for delimiters.
-rw-r--r--doc/ScintillaDoc.html3
-rw-r--r--doc/ScintillaHistory.html4
-rw-r--r--src/Selection.cxx56
-rw-r--r--src/Selection.h4
-rw-r--r--test/unit/testSelection.cxx28
5 files changed, 54 insertions, 41 deletions
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html
index 2d0cd4679..5c92043cc 100644
--- a/doc/ScintillaDoc.html
+++ b/doc/ScintillaDoc.html
@@ -1444,6 +1444,9 @@ struct Sci_TextRangeFull {
<b id="SCI_GETSELECTIONSERIALIZED">SCI_GETSELECTIONSERIALIZED(&lt;unused&gt;, char *selectionString) &rarr; position</b><br />
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.</p>
+ <p>The format is currently<br/><code>[selType:R|L|T] [# mainRange ,] [anchor [v virtualSpace] [- caret [v virtualSpace]]] [, ...]</code>
+ <br/>Example of a multiple selection with virtual space: <code>#1,5v3-2,1</code>
+ </p>
<p>
<b id="SC_ELEMENT_SELECTION_ADDITIONAL_TEXT">SC_ELEMENT_SELECTION_ADDITIONAL_TEXT : colouralpha</b><br />
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html
index 0e04865d8..e8b55793b 100644
--- a/doc/ScintillaHistory.html
+++ b/doc/ScintillaHistory.html
@@ -602,6 +602,10 @@
Released 8 June 2025.
</li>
<li>
+ Change format for SCI_GETSELECTIONSERIALIZED.
+ <a href="https://sourceforge.net/p/scintilla/feature-requests/1563/">Feature #1563</a>.
+ </li>
+ <li>
On Win32, force autocompletion list colours to be opaque.
Enlarge bitmap to avoid visible blank background between items.
<a href="https://sourceforge.net/p/scintilla/bugs/2482/">Bug #2482</a>.
diff --git a/src/Selection.cxx b/src/Selection.cxx
index 795570ee2..967033bff 100644
--- a/src/Selection.cxx
+++ b/src/Selection.cxx
@@ -29,23 +29,24 @@ namespace {
// Generically convert a string to a integer value throwing if the conversion failed.
// Failures include values that are out of range for the destination variable.
template <typename T>
-void ValueFromString(std::string_view sv, T &value) {
+void ValueFromString(std::string_view &sv, T &value) {
const std::from_chars_result res = std::from_chars(sv.data(), sv.data() + sv.size(), value);
if (res.ec != std::errc{}) {
if (res.ec == std::errc::result_out_of_range)
throw std::runtime_error("from_chars out of range.");
throw std::runtime_error("from_chars failed.");
}
+ sv.remove_prefix(res.ptr - sv.data());
}
}
-SelectionPosition::SelectionPosition(std::string_view sv) : position(0) {
- if (const size_t v = sv.find('v'); v != std::string_view::npos) {
- ValueFromString(sv.substr(v + 1), virtualSpace);
- sv = sv.substr(0, v);
- }
+SelectionPosition::SelectionPosition(std::string_view &sv) : position(0) {
ValueFromString(sv, position);
+ if (!sv.empty() && sv.front() == 'v') {
+ sv.remove_prefix(1);
+ ValueFromString(sv, virtualSpace);
+ }
}
void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept {
@@ -109,14 +110,13 @@ std::string SelectionPosition::ToString() const {
return result;
}
-SelectionRange::SelectionRange(std::string_view sv) {
- const size_t dash = sv.find('-');
- if (dash == std::string_view::npos) {
- anchor = SelectionPosition(sv);
+SelectionRange::SelectionRange(std::string_view &sv) {
+ anchor = SelectionPosition(sv);
+ if (sv.empty() || sv.front() != '-') {
caret = anchor;
} else {
- anchor = SelectionPosition(sv.substr(0, dash));
- caret = SelectionPosition(sv.substr(dash + 1));
+ sv.remove_prefix(1);
+ caret = SelectionPosition(sv);
}
}
@@ -274,10 +274,13 @@ Selection::Selection(std::string_view sv) : mainRange(0), moveExtends(false), te
sv.remove_prefix(1);
}
- // Non-zero main index at end after '#'
- if (const size_t hash = sv.find('#'); hash != std::string_view::npos) {
- ValueFromString(sv.substr(hash + 1), mainRange);
- sv = sv.substr(0, hash);
+ // Non-zero main index at start after '#'
+ if (!sv.empty() && sv.front() == '#') {
+ sv.remove_prefix(1);
+ ValueFromString(sv, mainRange);
+ if (!sv.empty() && sv.front() == ',') {
+ sv.remove_prefix(1);
+ }
}
// Remainder is list of ranges
@@ -288,13 +291,12 @@ Selection::Selection(std::string_view sv) : mainRange(0), moveExtends(false), te
ranges.emplace_back(SelectionPosition(0));
}
} else {
- size_t comma = sv.find(',');
- while (comma != std::string_view::npos) {
- ranges.emplace_back(sv.substr(0, comma));
- sv.remove_prefix(comma + 1);
- comma = sv.find(',');
+ while (!sv.empty()) {
+ if (sv.front() == ',') {
+ sv.remove_prefix(1);
+ }
+ ranges.emplace_back(sv);
}
- ranges.emplace_back(sv);
if (mainRange >= ranges.size()) {
mainRange = ranges.size() - 1;
}
@@ -595,6 +597,11 @@ std::string Selection::ToString() const {
// No prefix.
break;
}
+ if (mainRange > 0) {
+ result += '#';
+ result += std::to_string(mainRange);
+ result += ',';
+ }
if (selType == SelTypes::rectangle || selType == SelTypes::thin) {
result += rangeRectangular.ToString();
} else {
@@ -606,10 +613,5 @@ std::string Selection::ToString() const {
}
}
- if (mainRange > 0) {
- result += '#';
- result += std::to_string(mainRange);
- }
-
return result;
}
diff --git a/src/Selection.h b/src/Selection.h
index 6779ee7ce..1a57d2e78 100644
--- a/src/Selection.h
+++ b/src/Selection.h
@@ -20,7 +20,7 @@ public:
if (virtualSpace < 0)
virtualSpace = 0;
}
- explicit SelectionPosition(std::string_view sv);
+ explicit SelectionPosition(std::string_view &sv);
void Reset() noexcept {
position = 0;
virtualSpace = 0;
@@ -121,7 +121,7 @@ struct SelectionRange {
}
constexpr SelectionRange(Sci::Position caret_, Sci::Position anchor_) noexcept : caret(caret_), anchor(anchor_) {
}
- explicit SelectionRange(std::string_view sv);
+ explicit SelectionRange(std::string_view &sv);
SelectionSegment AsSegment() const noexcept {
return {caret, anchor};
}
diff --git a/test/unit/testSelection.cxx b/test/unit/testSelection.cxx
index 609feaf00..ab0065624 100644
--- a/test/unit/testSelection.cxx
+++ b/test/unit/testSelection.cxx
@@ -115,18 +115,21 @@ TEST_CASE("SelectionPosition") {
const std::string invalidString(invalid.ToString());
REQUIRE(invalidString == "-1");
- const SelectionPosition invalidReturned(invalidString);
+ std::string_view sv = invalidString;
+ const SelectionPosition invalidReturned(sv);
REQUIRE(invalidReturned == invalid);
const std::string zeroString(zero.ToString());
REQUIRE(zeroString == "0");
- const SelectionPosition zeroReturned(zeroString);
+ sv = zeroString;
+ const SelectionPosition zeroReturned(sv);
REQUIRE(zeroReturned == zero);
const SelectionPosition virtue(2, 3);
const std::string virtueString(virtue.ToString());
REQUIRE(virtueString == "2v3");
- const SelectionPosition virtueReturned(virtueString);
+ sv = virtueString;
+ const SelectionPosition virtueReturned(sv);
REQUIRE(virtueReturned == virtue);
}
@@ -156,9 +159,10 @@ TEST_CASE("SelectionRange") {
// Range from 1 to 2 with 3 virtual spaces
const SelectionRange range123(SelectionPosition(2, 3), SelectionPosition(1));
const std::string range123String(range123.ToString());
- // Opposite order to constructor: from anchor to caret
+ // Opposite order to constructor: from anchor to caret
REQUIRE(range123String == "1-2v3");
- const SelectionRange range123Returned(range123String);
+ std::string_view sv = range123String;
+ const SelectionRange range123Returned(sv);
REQUIRE(range123Returned == range123);
}
@@ -200,7 +204,7 @@ TEST_CASE("Selection") {
SECTION("Selection") {
Selection sel;
-
+
REQUIRE(sel.selType == Selection::SelTypes::stream);
REQUIRE(!sel.IsRectangular());
REQUIRE(sel.Count() == 1);
@@ -220,9 +224,9 @@ TEST_CASE("Selection") {
Selection selection;
selection.SetSelection(range532);
const std::string selectionString(selection.ToString());
- // Opposite order to constructor: from anchor to caret
+ // Opposite order to constructor: from anchor to caret
REQUIRE(selectionString == "5v3-2");
- const SelectionRange selectionReturned(selectionString);
+ selection = Selection(selectionString);
REQUIRE(selection.selType == Selection::SelTypes::stream);
REQUIRE(!selection.IsRectangular());
@@ -246,8 +250,8 @@ TEST_CASE("Selection") {
selection.AddSelection(range1);
selection.SetMain(1);
const std::string selectionString(selection.ToString());
- REQUIRE(selectionString == "5v3-2,1#1");
- const SelectionRange selectionReturned(selectionString);
+ REQUIRE(selectionString == "#1,5v3-2,1");
+ selection = Selection(selectionString);
REQUIRE(selection.selType == Selection::SelTypes::stream);
REQUIRE(!selection.IsRectangular());
@@ -266,7 +270,7 @@ TEST_CASE("Selection") {
// Range from 5 with 3 virtual spaces to 2
const SelectionRange range532(SelectionPosition(2), SelectionPosition(5, 3));
-
+
// Create a single-line rectangular selection
Selection selection;
selection.selType = Selection::SelTypes::rectangle;
@@ -276,7 +280,7 @@ TEST_CASE("Selection") {
const std::string selectionString(selection.ToString());
REQUIRE(selectionString == "R5v3-2");
- const Selection selectionReturned(selectionString);
+ selection = Selection(selectionString);
REQUIRE(selection.selType == Selection::SelTypes::rectangle);
REQUIRE(selection.IsRectangular());