aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Selection.cxx
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2025-02-01 09:07:57 +1100
committerNeil <nyamatongwe@gmail.com>2025-02-01 09:07:57 +1100
commitbd03d24f8bd44813db8ed1c73c5a46400d94a7cf (patch)
tree74c7984b59a193a54db66bd94d15c595a512ed0b /src/Selection.cxx
parent97691047e0c13b92639d340ee9f34dde67941e18 (diff)
downloadscintilla-mirror-bd03d24f8bd44813db8ed1c73c5a46400d94a7cf.tar.gz
Implement serialization of Selection to and from strings.
Requires std::from_chars to be available.
Diffstat (limited to 'src/Selection.cxx')
-rw-r--r--src/Selection.cxx145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/Selection.cxx b/src/Selection.cxx
index ad63afd70..5650d5c7e 100644
--- a/src/Selection.cxx
+++ b/src/Selection.cxx
@@ -9,11 +9,13 @@
#include <cstdlib>
#include <stdexcept>
+#include <string>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>
+#include <charconv>
#include "Debugging.h"
@@ -22,6 +24,30 @@
using namespace Scintilla::Internal;
+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) {
+ 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.");
+ }
+}
+
+}
+
+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);
+ }
+ ValueFromString(sv, position);
+}
+
void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept {
if (insertion) {
if (position == startChange) {
@@ -73,6 +99,26 @@ bool SelectionPosition::operator >=(const SelectionPosition &other) const noexce
return *this > other;
}
+std::string SelectionPosition::ToString() const {
+ std::string result = std::to_string(position);
+ if (virtualSpace) {
+ result += 'v';
+ result += std::to_string(virtualSpace);
+ }
+ return result;
+}
+
+SelectionRange::SelectionRange(std::string_view sv) {
+ const size_t dash = sv.find('-');
+ if (dash == std::string_view::npos) {
+ anchor = SelectionPosition(sv);
+ caret = anchor;
+ } else {
+ anchor = SelectionPosition(sv.substr(0, dash));
+ caret = SelectionPosition(sv.substr(dash + 1));
+ }
+}
+
Sci::Position SelectionRange::Length() const noexcept {
if (anchor > caret) {
return anchor.Position() - caret.Position();
@@ -190,10 +236,73 @@ void SelectionRange::MinimizeVirtualSpace() noexcept {
}
}
+std::string SelectionRange::ToString() const {
+ std::string result = anchor.ToString();
+ if (!(caret == anchor)) {
+ result += '-';
+ result += caret.ToString();
+ }
+ return result;
+}
+
Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(SelTypes::stream) {
AddSelection(SelectionRange(SelectionPosition(0)));
}
+Selection::Selection(std::string_view sv) : mainRange(0), moveExtends(false), tentativeMain(false), selType(SelTypes::stream) {
+ if (sv.empty()) {
+ return;
+ }
+ try {
+ // Decode initial letter prefix if any
+ switch (sv.front()) {
+ case 'R':
+ selType = SelTypes::rectangle;
+ break;
+ case 'L':
+ selType = SelTypes::lines;
+ break;
+ case 'T':
+ selType = SelTypes::thin;
+ break;
+ default:
+ break;
+ }
+ if (selType != SelTypes::stream) {
+ 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);
+ }
+
+ // Remainder is list of ranges
+ if (selType == SelTypes::rectangle || selType == SelTypes::thin) {
+ rangeRectangular = SelectionRange(sv);
+ // Ensure enough ranges exist for mainRange to be in bounds
+ for (size_t i = 0; i <= mainRange; i++) {
+ 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(',');
+ }
+ ranges.emplace_back(sv);
+ if (mainRange >= ranges.size()) {
+ mainRange = ranges.size() - 1;
+ }
+ }
+ } catch (std::runtime_error &) {
+ // On failure, produce an empty selection.
+ Clear();
+ }
+}
+
bool Selection::IsRectangular() const noexcept {
return (selType == SelTypes::rectangle) || (selType == SelTypes::thin);
}
@@ -458,3 +567,39 @@ void Selection::RotateMain() noexcept {
void Selection::SetRanges(const Ranges &rangesToSet) {
ranges = rangesToSet;
}
+
+std::string Selection::ToString() const {
+ std::string result;
+ switch (selType) {
+ case SelTypes::rectangle:
+ result += 'R';
+ break;
+ case SelTypes::lines:
+ result += 'L';
+ break;
+ case SelTypes::thin:
+ result += 'T';
+ break;
+ default:
+ // No handling of none as not a real value of enumeration, just used for empty arguments
+ // No prefix.
+ break;
+ }
+ if (selType == SelTypes::rectangle || selType == SelTypes::thin) {
+ result += rangeRectangular.ToString();
+ } else {
+ for (size_t r = 0; r < ranges.size(); r++) {
+ if (r > 0) {
+ result += ',';
+ }
+ result += ranges[r].ToString();
+ }
+ }
+
+ if (mainRange > 0) {
+ result += '#';
+ result += std::to_string(mainRange);
+ }
+
+ return result;
+}