chore: bump scintilla and lexilla version
This commit is contained in:
615
3rdparty/scintilla557/scintilla/src/Selection.cxx
vendored
Normal file
615
3rdparty/scintilla557/scintilla/src/Selection.cxx
vendored
Normal file
@ -0,0 +1,615 @@
|
||||
// Scintilla source code edit control
|
||||
/** @file Selection.cxx
|
||||
** Classes maintaining the selection.
|
||||
**/
|
||||
// Copyright 2009 by Neil Hodgson <neilh@scintilla.org>
|
||||
// The License.txt file describes the conditions under which this software may be distributed.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <charconv>
|
||||
|
||||
#include "Debugging.h"
|
||||
|
||||
#include "Position.h"
|
||||
#include "Selection.h"
|
||||
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
} else {
|
||||
if (position == startChange) {
|
||||
virtualSpace = 0;
|
||||
}
|
||||
if (position > startChange) {
|
||||
const Sci::Position endDeletion = startChange + length;
|
||||
if (position > endDeletion) {
|
||||
position -= length;
|
||||
} else {
|
||||
position = startChange;
|
||||
virtualSpace = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectionPosition::operator >(const SelectionPosition &other) const noexcept {
|
||||
if (position == other.position)
|
||||
return virtualSpace > other.virtualSpace;
|
||||
return position > other.position;
|
||||
}
|
||||
|
||||
bool SelectionPosition::operator <=(const SelectionPosition &other) const noexcept {
|
||||
if (other == *this)
|
||||
return true;
|
||||
return other > *this;
|
||||
}
|
||||
|
||||
bool SelectionPosition::operator >=(const SelectionPosition &other) const noexcept {
|
||||
if (other == *this)
|
||||
return true;
|
||||
return *this > other;
|
||||
}
|
||||
|
||||
double SelectionPosition::VirtualSpaceWidth(double spaceWidth) const noexcept {
|
||||
return static_cast<double>(virtualSpace) * spaceWidth;
|
||||
}
|
||||
|
||||
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();
|
||||
} else {
|
||||
return caret.Position() - anchor.Position();
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionRange::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept {
|
||||
// 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.Position() < anchor.Position();
|
||||
const bool anchorStart = anchor.Position() < caret.Position();
|
||||
|
||||
caret.MoveForInsertDelete(insertion, startChange, length, caretStart);
|
||||
anchor.MoveForInsertDelete(insertion, startChange, length, anchorStart);
|
||||
}
|
||||
|
||||
bool SelectionRange::Contains(Sci::Position pos) const noexcept {
|
||||
if (anchor > caret)
|
||||
return (pos >= caret.Position()) && (pos <= anchor.Position());
|
||||
else
|
||||
return (pos >= anchor.Position()) && (pos <= caret.Position());
|
||||
}
|
||||
|
||||
bool SelectionRange::Contains(SelectionPosition sp) const noexcept {
|
||||
if (anchor > caret)
|
||||
return (sp >= caret) && (sp <= anchor);
|
||||
else
|
||||
return (sp >= anchor) && (sp <= caret);
|
||||
}
|
||||
|
||||
bool SelectionRange::ContainsCharacter(Sci::Position posCharacter) const noexcept {
|
||||
if (anchor > caret)
|
||||
return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
|
||||
else
|
||||
return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
|
||||
}
|
||||
|
||||
bool SelectionRange::ContainsCharacter(SelectionPosition spCharacter) const noexcept {
|
||||
if (anchor > caret)
|
||||
return (spCharacter >= caret) && (spCharacter < anchor);
|
||||
else
|
||||
return (spCharacter >= anchor) && (spCharacter < caret);
|
||||
}
|
||||
|
||||
SelectionSegment SelectionRange::Intersect(SelectionSegment check) const noexcept {
|
||||
const SelectionSegment inOrder = AsSegment();
|
||||
if ((inOrder.start > check.end) || (inOrder.end < check.start)) {
|
||||
// Nothing in common, not even touching so return empty *invalid* segment
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
std::max(check.start, inOrder.start),
|
||||
std::min(check.end, inOrder.end)
|
||||
};
|
||||
}
|
||||
|
||||
void SelectionRange::Swap() noexcept {
|
||||
std::swap(caret, anchor);
|
||||
}
|
||||
|
||||
bool SelectionRange::Trim(SelectionRange range) noexcept {
|
||||
const SelectionPosition startRange = range.Start();
|
||||
const SelectionPosition endRange = range.End();
|
||||
SelectionPosition start = Start();
|
||||
SelectionPosition end = End();
|
||||
PLATFORM_ASSERT(start <= end);
|
||||
PLATFORM_ASSERT(startRange <= endRange);
|
||||
if ((startRange <= end) && (endRange >= start)) {
|
||||
if ((start > startRange) && (end < endRange)) {
|
||||
// Completely covered by range -> empty at start
|
||||
end = start;
|
||||
} else if ((start < startRange) && (end > endRange)) {
|
||||
// Completely covers range -> empty at start
|
||||
end = start;
|
||||
} else if (start <= startRange) {
|
||||
// Trim end
|
||||
end = startRange;
|
||||
} else { //
|
||||
PLATFORM_ASSERT(end >= endRange);
|
||||
// Trim start
|
||||
start = endRange;
|
||||
}
|
||||
if (anchor > caret) {
|
||||
caret = start;
|
||||
anchor = end;
|
||||
} else {
|
||||
anchor = start;
|
||||
caret = end;
|
||||
}
|
||||
return Empty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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()) {
|
||||
Sci::Position virtualSpace = caret.VirtualSpace();
|
||||
if (virtualSpace > anchor.VirtualSpace())
|
||||
virtualSpace = anchor.VirtualSpace();
|
||||
caret.SetVirtualSpace(virtualSpace);
|
||||
anchor.SetVirtualSpace(virtualSpace);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Sci::Position Selection::MainCaret() const noexcept {
|
||||
return ranges[mainRange].caret.Position();
|
||||
}
|
||||
|
||||
Sci::Position Selection::MainAnchor() const noexcept {
|
||||
return ranges[mainRange].anchor.Position();
|
||||
}
|
||||
|
||||
SelectionRange &Selection::Rectangular() noexcept {
|
||||
return rangeRectangular;
|
||||
}
|
||||
|
||||
SelectionRange Selection::RectangularCopy() const noexcept {
|
||||
return rangeRectangular;
|
||||
}
|
||||
|
||||
SelectionSegment Selection::Limits() const noexcept {
|
||||
PLATFORM_ASSERT(!ranges.empty());
|
||||
SelectionSegment sr = ranges[0].AsSegment();
|
||||
for (size_t i=1; i<ranges.size(); i++) {
|
||||
sr.Extend(ranges[i].anchor);
|
||||
sr.Extend(ranges[i].caret);
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
|
||||
SelectionSegment Selection::LimitsForRectangularElseMain() const noexcept {
|
||||
if (IsRectangular()) {
|
||||
return Limits();
|
||||
}
|
||||
return ranges[mainRange].AsSegment();
|
||||
}
|
||||
|
||||
size_t Selection::Count() const noexcept {
|
||||
return ranges.size();
|
||||
}
|
||||
|
||||
size_t Selection::Main() const noexcept {
|
||||
return mainRange;
|
||||
}
|
||||
|
||||
void Selection::SetMain(size_t r) noexcept {
|
||||
PLATFORM_ASSERT(r < ranges.size());
|
||||
mainRange = r;
|
||||
}
|
||||
|
||||
SelectionRange &Selection::Range(size_t r) noexcept {
|
||||
return ranges[r];
|
||||
}
|
||||
|
||||
const SelectionRange &Selection::Range(size_t r) const noexcept {
|
||||
return ranges[r];
|
||||
}
|
||||
|
||||
SelectionRange &Selection::RangeMain() noexcept {
|
||||
return ranges[mainRange];
|
||||
}
|
||||
|
||||
const SelectionRange &Selection::RangeMain() const noexcept {
|
||||
return ranges[mainRange];
|
||||
}
|
||||
|
||||
SelectionPosition Selection::Start() const noexcept {
|
||||
if (IsRectangular()) {
|
||||
return rangeRectangular.Start();
|
||||
}
|
||||
return ranges[mainRange].Start();
|
||||
}
|
||||
|
||||
bool Selection::MoveExtends() const noexcept {
|
||||
return moveExtends;
|
||||
}
|
||||
|
||||
void Selection::SetMoveExtends(bool moveExtends_) noexcept {
|
||||
moveExtends = moveExtends_;
|
||||
}
|
||||
|
||||
bool Selection::Empty() const noexcept {
|
||||
for (const SelectionRange &range : ranges) {
|
||||
if (!range.Empty())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SelectionPosition Selection::Last() const noexcept {
|
||||
SelectionPosition lastPosition;
|
||||
for (const SelectionRange &range : ranges) {
|
||||
if (lastPosition < range.caret)
|
||||
lastPosition = range.caret;
|
||||
if (lastPosition < range.anchor)
|
||||
lastPosition = range.anchor;
|
||||
}
|
||||
return lastPosition;
|
||||
}
|
||||
|
||||
Sci::Position Selection::Length() const noexcept {
|
||||
Sci::Position len = 0;
|
||||
for (const SelectionRange &range : ranges) {
|
||||
len += range.Length();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void Selection::MovePositions(bool insertion, Sci::Position startChange, Sci::Position length) noexcept {
|
||||
for (SelectionRange &range : ranges) {
|
||||
range.MoveForInsertDelete(insertion, startChange, length);
|
||||
}
|
||||
if (selType == SelTypes::rectangle) {
|
||||
rangeRectangular.MoveForInsertDelete(insertion, startChange, length);
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::TrimSelection(SelectionRange range) noexcept {
|
||||
for (size_t i=0; i<ranges.size();) {
|
||||
if ((i != mainRange) && (ranges[i].Trim(range))) {
|
||||
// Trimmed to empty so remove
|
||||
for (size_t j=i; j<ranges.size()-1; j++) {
|
||||
ranges[j] = ranges[j+1];
|
||||
if (j == mainRange-1)
|
||||
mainRange--;
|
||||
}
|
||||
ranges.pop_back();
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::TrimOtherSelections(size_t r, SelectionRange range) noexcept {
|
||||
for (size_t i = 0; i<ranges.size(); ++i) {
|
||||
if (i != r) {
|
||||
ranges[i].Trim(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::SetSelection(SelectionRange range) noexcept {
|
||||
if (ranges.size() > 1) {
|
||||
ranges.erase(ranges.begin() + 1, ranges.end());
|
||||
}
|
||||
ranges[0] = range;
|
||||
mainRange = 0;
|
||||
}
|
||||
|
||||
void Selection::AddSelection(SelectionRange range) {
|
||||
TrimSelection(range);
|
||||
ranges.push_back(range);
|
||||
mainRange = ranges.size() - 1;
|
||||
}
|
||||
|
||||
void Selection::AddSelectionWithoutTrim(SelectionRange range) {
|
||||
ranges.push_back(range);
|
||||
mainRange = ranges.size() - 1;
|
||||
}
|
||||
|
||||
void Selection::DropSelection(size_t r) noexcept {
|
||||
if ((ranges.size() > 1) && (r < ranges.size())) {
|
||||
size_t mainNew = mainRange;
|
||||
if (mainNew >= r) {
|
||||
if (mainNew == 0) {
|
||||
mainNew = ranges.size() - 2;
|
||||
} else {
|
||||
mainNew--;
|
||||
}
|
||||
}
|
||||
ranges.erase(ranges.begin() + r);
|
||||
mainRange = mainNew;
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::DropAdditionalRanges() noexcept {
|
||||
SetSelection(RangeMain());
|
||||
}
|
||||
|
||||
void Selection::TentativeSelection(SelectionRange range) {
|
||||
if (!tentativeMain) {
|
||||
rangesSaved = ranges;
|
||||
}
|
||||
ranges = rangesSaved;
|
||||
AddSelection(range);
|
||||
TrimSelection(ranges[mainRange]);
|
||||
tentativeMain = true;
|
||||
}
|
||||
|
||||
void Selection::CommitTentative() noexcept {
|
||||
rangesSaved.clear();
|
||||
tentativeMain = false;
|
||||
}
|
||||
|
||||
InSelection Selection::RangeType(size_t r) const noexcept {
|
||||
return r == Main() ? InSelection::inMain : InSelection::inAdditional;
|
||||
}
|
||||
|
||||
InSelection Selection::CharacterInSelection(Sci::Position posCharacter) const noexcept {
|
||||
for (size_t i=0; i<ranges.size(); i++) {
|
||||
if (ranges[i].ContainsCharacter(posCharacter))
|
||||
return RangeType(i);
|
||||
}
|
||||
return InSelection::inNone;
|
||||
}
|
||||
|
||||
InSelection Selection::InSelectionForEOL(Sci::Position pos) const noexcept {
|
||||
for (size_t i=0; i<ranges.size(); i++) {
|
||||
if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))
|
||||
return RangeType(i);
|
||||
}
|
||||
return InSelection::inNone;
|
||||
}
|
||||
|
||||
Sci::Position Selection::VirtualSpaceFor(Sci::Position pos) const noexcept {
|
||||
Sci::Position virtualSpace = 0;
|
||||
for (const SelectionRange &range : ranges) {
|
||||
if ((range.caret.Position() == pos) && (virtualSpace < range.caret.VirtualSpace()))
|
||||
virtualSpace = range.caret.VirtualSpace();
|
||||
if ((range.anchor.Position() == pos) && (virtualSpace < range.anchor.VirtualSpace()))
|
||||
virtualSpace = range.anchor.VirtualSpace();
|
||||
}
|
||||
return virtualSpace;
|
||||
}
|
||||
|
||||
void Selection::Clear() noexcept {
|
||||
if (ranges.size() > 1) {
|
||||
ranges.erase(ranges.begin() + 1, ranges.end());
|
||||
}
|
||||
ranges[0].Reset();
|
||||
rangesSaved.clear();
|
||||
rangeRectangular.Reset();
|
||||
mainRange = 0;
|
||||
moveExtends = false;
|
||||
tentativeMain = false;
|
||||
selType = SelTypes::stream;
|
||||
}
|
||||
|
||||
void Selection::RemoveDuplicates() noexcept {
|
||||
for (size_t i=0; i<ranges.size()-1; i++) {
|
||||
if (ranges[i].Empty()) {
|
||||
size_t j=i+1;
|
||||
while (j<ranges.size()) {
|
||||
if (ranges[i] == ranges[j]) {
|
||||
ranges.erase(ranges.begin() + j);
|
||||
if (mainRange >= j)
|
||||
mainRange--;
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::RotateMain() noexcept {
|
||||
mainRange = (mainRange + 1) % ranges.size();
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
Reference in New Issue
Block a user