+upd: Scintilla v5.5.4

This commit is contained in:
METANEOCORTEX\Kotti 2025-02-02 13:06:07 +01:00
parent 5744dd52f6
commit 4c1add22c0
34 changed files with 563 additions and 174 deletions

View File

@ -678,8 +678,8 @@ BEGIN
CONTROL "&Diese Nachricht nicht nochmals anzeigen", IDC_INFOBOXCHECK,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,7,66,122,10
DEFPUSHBUTTON "&Neu Laden", IDRETRY, 186, 82, 50, 14
PUSHBUTTON "&Ignore", IDCANCEL, 130, 82, 50, 14
PUSHBUTTON "&Monitor", IDCONTINUE, 242, 82, 50, 14
PUSHBUTTON "&Ignorieren", IDCANCEL, 130, 82, 50, 14
PUSHBUTTON "&Monitoring", IDCONTINUE, 242, 82, 50, 14
END
IDD_MUI_SORT DIALOGEX 0, 0, 185, 188

View File

@ -179,7 +179,7 @@ BEGIN
IDS_MUI_ASK_CLEAR_UNDO "Diese Operation wird die Undo-History löschen. Weiter machen?"
IDS_MUI_READONLY_SAVE """%s"" ist schreibgeschützt. Dokument unter einem anderen Dateipfad speichern?"
IDS_MUI_FILECHANGENOTIFY
"Die geladene Datei wurde durch ein anderes Programm auf dem Datenträger verändert. Bitte wähle:\n\nIgnore - \t\tIgnoriere alle Änderungen\nNeu Laden - \tDatei neu laden\nMonitor - \t\tWechsle zum Logfile Monitoring"
"Die geladene Datei wurde durch ein anderes Programm auf dem Datenträger verändert. Bitte wähle:\n\nIgnorieren:\tIgnoriere weitere Änderungen\nNeu Laden:\tDatei neu laden\nMonitoring:\tWechsle zum Logfile Monitoring"
IDS_MUI_FILECHANGENOTIFY2 "Die geladene Datei wurde auf dem Datenträger gelöscht. Nochmals speichern?"
IDS_MUI_FILELOCK_ERROR "Es konnte keine exklusive Dateisperre auf ""%s"" gesetzt werden!"
IDS_MUI_STICKYWINPOS "Eingefrorene Fensterpositionierung is eingeschaltet worden. Jede neue Notepad3 Instanz wird nun diese Fensterpositionierung initial verwenden"

View File

@ -1275,6 +1275,14 @@ ChangeHistoryOption ScintillaCall::ChangeHistory() {
return static_cast<Scintilla::ChangeHistoryOption>(Call(Message::GetChangeHistory));
}
void ScintillaCall::SetUndoSelectionHistory(Scintilla::UndoSelectionHistoryOption undoSelectionHistory) {
Call(Message::SetUndoSelectionHistory, static_cast<uintptr_t>(undoSelectionHistory));
}
UndoSelectionHistoryOption ScintillaCall::UndoSelectionHistory() {
return static_cast<Scintilla::UndoSelectionHistoryOption>(Call(Message::GetUndoSelectionHistory));
}
Line ScintillaCall::FirstVisibleLine() {
return Call(Message::GetFirstVisibleLine);
}

View File

@ -1121,7 +1121,7 @@ struct Sci_TextRangeFull {
If the text argument is NULL(0) then the length that should be allocated
to store the entire current line is returned.</p>
<p>See also: <code
<p>See also: <code>
<a class="seealso" href="#SCI_GETSELTEXT">SCI_GETSELTEXT</a>,
<a class="seealso" href="#SCI_GETLINE">SCI_GETLINE</a>,
<a class="seealso" href="#SCI_GETTEXT">SCI_GETTEXT</a>,
@ -1938,6 +1938,8 @@ struct Sci_TextToFindFull {
<a class="message" href="#SCI_ENDUNDOACTION">SCI_ENDUNDOACTION</a><br />
<a class="message" href="#SCI_GETUNDOSEQUENCE">SCI_GETUNDOSEQUENCE &rarr; int</a><br />
<a class="message" href="#SCI_ADDUNDOACTION">SCI_ADDUNDOACTION(int token, int flags)</a><br />
<a class="message" href="#SCI_SETUNDOSELECTIONHISTORY">SCI_SETUNDOSELECTIONHISTORY(int undoSelectionHistory)</a><br />
<a class="message" href="#SCI_GETUNDOSELECTIONHISTORY">SCI_GETUNDOSELECTIONHISTORY &rarr; int</a><br />
</code>
<p><b id="SCI_UNDO">SCI_UNDO</b><br />
@ -2016,6 +2018,35 @@ struct Sci_TextToFindFull {
look like typing or deletions that look like multiple uses of the Backspace or Delete keys.
</p>
<p><b id="SCI_SETUNDOSELECTIONHISTORY">SCI_SETUNDOSELECTIONHISTORY(int undoSelectionHistory)</b><br />
<b id="SCI_GETUNDOSELECTIONHISTORY">SCI_GETUNDOSELECTIONHISTORY &rarr; int</b><br />
The selection for each action can be saved and then restored when undo or redo is performed.
<code>SCI_SETUNDOSELECTIONHISTORY</code> controls this.
There is a memory cost for this feature with a minimum of 150 bytes for each of undo and redo for each recorded action.
Recording may be turned on at any time.</p>
<p>The <code class="parameter">undoSelectionHistory</code> argument can be:</p>
<table class="standard" summary="Undo selection history state">
<tbody valign="top">
<tr>
<th align="left"><code>SC_UNDO_SELECTION_HISTORY_DISABLED</code></th>
<td>0</td>
<td>The default: undo selection history turned off.</td>
</tr>
<tr>
<th align="left"><code>SC_UNDO_SELECTION_HISTORY_ENABLED</code></th>
<td>1</td>
<td>Restore selection for each undo and redo.</td>
</tr>
</tbody>
</table>
<h2 id="UndoSaveRestore">Undo Save and Restore</h2>
<p>This feature is unfinished and has limitations.
@ -5216,7 +5247,7 @@ struct Sci_TextToFindFull {
<td><code>SC_SUPPORTS_LINE_DRAWS_FINAL</code></td>
<td>0</td>
<td>Whether drawing a line draws its final position.<br />
Only false on Win32 GDI.</div></td>
Only false on Win32 GDI.</td>
</tr>
<tr>
@ -7523,7 +7554,7 @@ sptr_t CallScintilla(unsigned int iMessage, uptr_t wParam, sptr_t lParam){
<p>For many applications lexing documents larger than 4GB will be too sluggish so
<code>SC_DOCUMENTOPTION_STYLES_NONE</code>
and the null lexer <code>"null"</code> can be used. Another approach is to turn on idle styling with
<code><a class="seealso" href="#SCI_SETIDLESTYLING">SCI_SETIDLESTYLING</code></a>.
<a class="seealso" href="#SCI_SETIDLESTYLING"><code>SCI_SETIDLESTYLING</code></a>.
</p>
<table class="standard" summary="Document options">

View File

@ -26,9 +26,9 @@
<table bgcolor="#CCCCCC" width="100%" cellspacing="0" cellpadding="8" border="0">
<tr>
<td>
<font size="4"> <a href="https://www.scintilla.org/scintilla553.zip">
<font size="4"> <a href="https://www.scintilla.org/scintilla554.zip">
Windows</a>&nbsp;&nbsp;
<a href="https://www.scintilla.org/scintilla553.tgz">
<a href="https://www.scintilla.org/scintilla554.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
Release 5.5.3
Release 5.5.4
</h3>
<h4>
Source Code
@ -50,8 +50,8 @@
The source code package contains all of the source code for Scintilla but no binary
executable code and is available in
<ul>
<li><a href="https://www.scintilla.org/scintilla553.zip">zip format</a> (1.8M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla553.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li>
<li><a href="https://www.scintilla.org/scintilla554.zip">zip format</a> (1.8M) commonly used on Windows</li>
<li><a href="https://www.scintilla.org/scintilla554.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li>
</ul>
Instructions for building on both Windows and Linux are included in the readme file.
<h4>

View File

@ -587,12 +587,29 @@
</tr>
</table>
<h2>Releases</h2>
<h3>
<a href="https://www.scintilla.org/scintilla555.zip">Release 5.5.5</a>
</h3>
<ul>
<li>
Released 18 December 2024.
</li>
<li>
Remember selection with undo and redo. Controlled with SCI_SETUNDOSELECTIONHISTORY.
<a href="https://sourceforge.net/p/scintilla/feature-requests/1273/">Feature #1273</a>,
<a href="https://sourceforge.net/p/scintilla/bugs/1479/">Bug #1479</a>,
<a href="https://sourceforge.net/p/scintilla/bugs/1224/">Bug #1224</a>.
</li>
<li>
Fix bug on Qt where double-click stopped working when Scintilla instance had been running for weeks.
</li>
</ul>
<h3>
<a href="https://www.scintilla.org/scintilla554.zip">Release 5.5.4</a>
</h3>
<ul>
<li>
Released 19 October 2024.
Released 18 December 2024.
</li>
<li>
Update to Unicode 15.1.
@ -603,10 +620,18 @@
<a href="https://sourceforge.net/p/scintilla/feature-requests/1533/">Feature #1533</a>.
</li>
<li>
Improve performance of DBCS text.
<a href="https://sourceforge.net/p/scintilla/feature-requests/1535/">Feature #1535</a>.
</li>
<li>
Fix wrapping removed lines.
<a href="https://sourceforge.net/p/scintilla/bugs/2456/">Bug #2456</a>.
</li>
<li>
Fix moving line down to empty final line and moving empty final line up.
<a href="https://sourceforge.net/p/scintilla/bugs/2457/">Bug #2457</a>.
</li>
<li>
On GTK, allow middle click to insert multiple times within a document.
<a href="https://github.com/geany/geany/issues/2629">Geany Issue #2629</a>.
</li>

View File

@ -9,7 +9,7 @@
<meta name="keywords" content="Scintilla, SciTE, Editing Component, Text Editor" />
<meta name="Description"
content="www.scintilla.org is the home of the Scintilla editing component and SciTE text editor application." />
<meta name="Date.Modified" content="20241019" />
<meta name="Date.Modified" content="20241218" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
.logo {
@ -61,8 +61,8 @@
GTK, and macOS</font>
</td>
<td width="40%" align="right">
<font color="#FFCC99" size="3"> Release version 5.5.3<br />
Site last modified October 19 2024</font>
<font color="#FFCC99" size="3"> Release version 5.5.4<br />
Site last modified December 18 2024</font>
</td>
<td width="20%">
&nbsp;
@ -77,11 +77,11 @@
</tr>
</table>
<ul id="versionlist">
<li>Version 5.5.4 fixes wrapping of removed lines and edge cases for moving lines. On GTK, middle click can be repeated with one value.</li>
<li>Version 5.5.3 fixes horizontal scrolling with a touchpad on Win32.</li>
<li>Version 5.5.2 adds multiple selection copy with separator, a font stretch setting, and access to whether an undo sequence is active.</li>
<li>Version 5.5.1 adds SCI_CUTALLOWLINE and fixes a Win32 bug that caused the cursor to flicker.</li>
<li>Version 5.5.0 adds elements for inactive additional selections.</li>
<li>Version 5.4.3 fixes a redo bug.</li>
</ul>
<ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li>

View File

@ -550,6 +550,10 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SC_CHANGE_HISTORY_INDICATORS 4
#define SCI_SETCHANGEHISTORY 2780
#define SCI_GETCHANGEHISTORY 2781
#define SC_UNDO_SELECTION_HISTORY_DISABLED 0
#define SC_UNDO_SELECTION_HISTORY_ENABLED 1
#define SCI_SETUNDOSELECTIONHISTORY 2782
#define SCI_GETUNDOSELECTIONHISTORY 2783
#define SCI_GETFIRSTVISIBLELINE 2152
#define SCI_GETLINE 2153
#define SCI_GETLINECOUNT 2154

View File

@ -1355,6 +1355,16 @@ set void SetChangeHistory=2780(ChangeHistoryOption changeHistory,)
# Report change history status.
get ChangeHistoryOption GetChangeHistory=2781(,)
enu UndoSelectionHistoryOption=SC_UNDO_SELECTION_HISTORY_
val SC_UNDO_SELECTION_HISTORY_DISABLED=0
val SC_UNDO_SELECTION_HISTORY_ENABLED=1
# Enable or disable undo selection history.
set void SetUndoSelectionHistory=2782(UndoSelectionHistoryOption undoSelectionHistory,)
# Report undo selection history status.
get UndoSelectionHistoryOption GetUndoSelectionHistory=2783(,)
# Retrieve the display line at the top of the display.
get line GetFirstVisibleLine=2152(,)

View File

@ -364,6 +364,8 @@ public:
Position FormatRangeFull(bool draw, RangeToFormatFull *fr);
void SetChangeHistory(Scintilla::ChangeHistoryOption changeHistory);
Scintilla::ChangeHistoryOption ChangeHistory();
void SetUndoSelectionHistory(Scintilla::UndoSelectionHistoryOption undoSelectionHistory);
Scintilla::UndoSelectionHistoryOption UndoSelectionHistory();
Line FirstVisibleLine();
Position GetLine(Line line, char *text);
std::string GetLine(Line line);

View File

@ -289,6 +289,8 @@ enum class Message {
FormatRangeFull = 2777,
SetChangeHistory = 2780,
GetChangeHistory = 2781,
SetUndoSelectionHistory = 2782,
GetUndoSelectionHistory = 2783,
GetFirstVisibleLine = 2152,
GetLine = 2153,
GetLineCount = 2154,

View File

@ -300,6 +300,11 @@ enum class ChangeHistoryOption {
Indicators = 4,
};
enum class UndoSelectionHistoryOption {
Disabled = 0,
Enabled = 1,
};
enum class FoldLevel {
None = 0x0,
Base = 0x400,

View File

@ -93,7 +93,7 @@ bool AutoComplete::IsFillUpChar(char ch) const noexcept {
return ch && (fillUpChars.find(ch) != std::string::npos);
}
void AutoComplete::SetSeparator(char separator_) {
void AutoComplete::SetSeparator(char separator_) noexcept {
separator = separator_;
}
@ -101,7 +101,7 @@ char AutoComplete::GetSeparator() const noexcept {
return separator;
}
void AutoComplete::SetTypesep(char separator_) {
void AutoComplete::SetTypesep(char separator_) noexcept {
typesep = separator_;
}
@ -109,28 +109,32 @@ char AutoComplete::GetTypesep() const noexcept {
return typesep;
}
namespace {
struct Sorter {
AutoComplete *ac;
const bool ignoreCase;
const char *list;
std::vector<int> indices;
Sorter(AutoComplete *ac_, const char *list_) : ac(ac_), list(list_) {
Sorter(const AutoComplete *ac, const char *list_) : ignoreCase(ac->ignoreCase), list(list_) {
int i = 0;
if (!list[i]) {
// Empty list has a single empty member
indices.push_back(i); // word start
indices.push_back(i); // word end
}
const char separator = ac->GetSeparator();
const char typesep = ac->GetTypesep();
while (list[i]) {
indices.push_back(i); // word start
while (list[i] != ac->GetTypesep() && list[i] != ac->GetSeparator() && list[i])
while (list[i] != typesep && list[i] != separator && list[i])
++i;
indices.push_back(i); // word end
if (list[i] == ac->GetTypesep()) {
while (list[i] != ac->GetSeparator() && list[i])
if (list[i] == typesep) {
while (list[i] != separator && list[i])
++i;
}
if (list[i] == ac->GetSeparator()) {
if (list[i] == separator) {
++i;
// preserve trailing separator as blank entry
if (!list[i]) {
@ -142,34 +146,41 @@ struct Sorter {
indices.push_back(i); // index of last position
}
bool operator()(int a, int b) noexcept {
const int lenA = indices[a * 2 + 1] - indices[a * 2];
const int lenB = indices[b * 2 + 1] - indices[b * 2];
bool operator()(int a, int b) const noexcept {
const unsigned indexA = a * 2;
const unsigned indexB = b * 2;
const int lenA = indices[indexA + 1] - indices[indexA];
const int lenB = indices[indexB + 1] - indices[indexB];
const int len = std::min(lenA, lenB);
int cmp;
if (ac->ignoreCase)
cmp = CompareNCaseInsensitive(list + indices[a * 2], list + indices[b * 2], len);
if (ignoreCase)
cmp = CompareNCaseInsensitive(list + indices[indexA], list + indices[indexB], len);
else
cmp = strncmp(list + indices[a * 2], list + indices[b * 2], len);
cmp = strncmp(list + indices[indexA], list + indices[indexB], len);
if (cmp == 0)
cmp = lenA - lenB;
return cmp < 0;
}
};
void FillSortMatrix(std::vector<int> &sortMatrix, int itemCount) {
sortMatrix.clear();
for (int i = 0; i < itemCount; i++) {
sortMatrix.push_back(i);
}
}
}
void AutoComplete::SetList(const char *list) {
if (autoSort == Ordering::PreSorted) {
lb->SetList(list, separator, typesep);
sortMatrix.clear();
for (int i = 0; i < lb->Length(); ++i)
sortMatrix.push_back(i);
FillSortMatrix(sortMatrix, lb->Length());
return;
}
Sorter IndexSort(this, list);
sortMatrix.clear();
for (int i = 0; i < static_cast<int>(IndexSort.indices.size()) / 2; ++i)
sortMatrix.push_back(i);
const Sorter IndexSort(this, list);
FillSortMatrix(sortMatrix, static_cast<int>(IndexSort.indices.size() / 2));
std::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort);
if (autoSort == Ordering::Custom || sortMatrix.size() < 2) {
lb->SetList(list, separator, typesep);
@ -178,28 +189,25 @@ void AutoComplete::SetList(const char *list) {
}
std::string sortedList;
char item[maxItemLen];
for (size_t i = 0; i < sortMatrix.size(); ++i) {
int wordLen = IndexSort.indices[sortMatrix[i] * 2 + 2] - IndexSort.indices[sortMatrix[i] * 2];
if (wordLen > maxItemLen-2)
wordLen = maxItemLen - 2;
memcpy(item, list + IndexSort.indices[sortMatrix[i] * 2], wordLen);
if ((i+1) == sortMatrix.size()) {
const unsigned index = sortMatrix[i] * 2;
sortMatrix[i] = static_cast<int>(i);
// word length include trailing typesep and separator
const int wordLen = IndexSort.indices[index + 2] - IndexSort.indices[index];
const std::string_view item(list + IndexSort.indices[index], wordLen);
sortedList += item;
if ((i + 1) == sortMatrix.size()) {
// Last item so remove separator if present
if ((wordLen > 0) && (item[wordLen-1] == separator))
wordLen--;
if (!item.empty() && item.back() == separator) {
sortedList.pop_back();
}
} else {
// Item before last needs a separator
if ((wordLen == 0) || (item[wordLen-1] != separator)) {
item[wordLen] = separator;
wordLen++;
if (item.empty() || item.back() != separator) {
sortedList += separator;
}
}
item[wordLen] = '\0';
sortedList += item;
}
for (int i = 0; i < static_cast<int>(sortMatrix.size()); ++i)
sortMatrix[i] = i;
lb->SetList(sortedList.c_str(), separator, typesep);
}
@ -293,7 +301,7 @@ void AutoComplete::Select(const char *word) {
if (autoSort == Ordering::Custom) {
// Check for a logically earlier match
for (int i = location + 1; i <= end; ++i) {
std::string item = lb->GetValue(sortMatrix[i]);
const std::string item = lb->GetValue(sortMatrix[i]);
if (CompareNCaseInsensitive(word, item.c_str(), lenWord))
break;
if (sortMatrix[i] < sortMatrix[location] && !strncmp(word, item.c_str(), lenWord))

View File

@ -18,7 +18,6 @@ class AutoComplete {
std::string fillUpChars;
char separator;
char typesep; // Type separator
enum { maxItemLen=1000 };
std::vector<int> sortMatrix;
public:
@ -67,11 +66,11 @@ public:
bool IsFillUpChar(char ch) const noexcept;
/// The separator character is used when interpreting the list in SetList
void SetSeparator(char separator_);
void SetSeparator(char separator_) noexcept;
char GetSeparator() const noexcept;
/// The typesep character is used for separating the word from the type
void SetTypesep(char separator_);
void SetTypesep(char separator_) noexcept;
char GetTypesep() const noexcept;
/// The list string contains a sequence of words separated by the separator character

View File

@ -1076,6 +1076,10 @@ int CellBuffer::UndoSequenceDepth() const noexcept {
return uh->UndoSequenceDepth();
}
bool CellBuffer::AfterUndoSequenceStart() const noexcept {
return uh->AfterUndoSequenceStart();
}
void CellBuffer::AddUndoAction(Sci::Position token, bool mayCoalesce) {
bool startSequence = false;
uh->AppendAction(ActionType::container, token, nullptr, 0, startSequence, mayCoalesce);

View File

@ -171,6 +171,7 @@ public:
void BeginUndoAction(bool mayCoalesce=false) noexcept;
void EndUndoAction() noexcept;
int UndoSequenceDepth() const noexcept;
bool AfterUndoSequenceStart() const noexcept;
void AddUndoAction(Sci::Position token, bool mayCoalesce);
void DeleteUndoHistory() noexcept;

View File

@ -18,6 +18,7 @@
#include <string_view>
#include <vector>
#include <array>
#include <map>
#include <forward_list>
#include <optional>
#include <algorithm>
@ -833,15 +834,9 @@ Sci::Position Document::MovePositionOutsideChar(Sci::Position pos, Sci::Position
// Else invalid UTF-8 so return position of isolated trail byte
}
} else {
// Anchor DBCS calculations at start of line because start of line can
// not be a DBCS trail byte.
const Sci::Position posStartLine = LineStartPosition(pos);
if (pos == posStartLine)
return pos;
// Step back until a non-lead-byte is found.
Sci::Position posCheck = pos;
while ((posCheck > posStartLine) && IsDBCSLeadByteNoExcept(cb.CharAt(posCheck-1)))
while ((posCheck > 0) && IsDBCSLeadByteNoExcept(cb.CharAt(posCheck-1)))
posCheck--;
// Check from known start of character.
@ -916,14 +911,11 @@ Sci::Position Document::NextPosition(Sci::Position pos, int moveDir) const noexc
if (pos > cb.Length())
pos = cb.Length();
} else {
// Anchor DBCS calculations at start of line because start of line can
// not be a DBCS trail byte.
const Sci::Position posStartLine = LineStartPosition(pos);
// See http://msdn.microsoft.com/en-us/library/cc194792%28v=MSDN.10%29.aspx
// http://msdn.microsoft.com/en-us/library/cc194790.aspx
if ((pos - 1) <= posStartLine) {
return pos - 1;
} else if (IsDBCSLeadByteNoExcept(cb.CharAt(pos - 1))) {
// How to Go Backward in a DBCS String
// https://msdn.microsoft.com/en-us/library/cc194792.aspx
// DBCS-Enabled Programs vs. Non-DBCS-Enabled Programs
// https://msdn.microsoft.com/en-us/library/cc194790.aspx
if (IsDBCSLeadByteNoExcept(cb.CharAt(pos - 1))) {
// Should actually be trail byte
if (IsDBCSDualByteAt(pos - 2)) {
return pos - 2;
@ -934,7 +926,7 @@ Sci::Position Document::NextPosition(Sci::Position pos, int moveDir) const noexc
} else {
// Otherwise, step back until a non-lead-byte is found.
Sci::Position posTemp = pos - 1;
while (posStartLine <= --posTemp && IsDBCSLeadByteNoExcept(cb.CharAt(posTemp)))
while (--posTemp >= 0 && IsDBCSLeadByteNoExcept(cb.CharAt(posTemp)))
;
// Now posTemp+1 must point to the beginning of a character,
// so figure out whether we went back an even or an odd
@ -1329,6 +1321,10 @@ bool Document::DeleteChars(Sci::Position pos, Sci::Position len) {
} else {
enteredModification++;
if (!cb.IsReadOnly()) {
if (cb.IsCollectingUndo() && cb.CanRedo()) {
// Abandoning some undo actions so truncate any later selections
TruncateUndoComments(cb.UndoCurrent());
}
NotifyModified(
DocModification(
ModificationFlags::BeforeDelete | ModificationFlags::User,
@ -1382,6 +1378,10 @@ Sci::Position Document::InsertString(Sci::Position position, const char *s, Sci:
s = insertion.c_str();
insertLength = insertion.length();
}
if (cb.IsCollectingUndo() && cb.CanRedo()) {
// Abandoning some undo actions so truncate any later selections
TruncateUndoComments(cb.UndoCurrent());
}
NotifyModified(
DocModification(
ModificationFlags::BeforeInsert | ModificationFlags::User,
@ -1565,6 +1565,20 @@ Sci::Position Document::Redo() {
return newPos;
}
void Document::EndUndoAction() noexcept {
cb.EndUndoAction();
if (UndoSequenceDepth() == 0) {
// Broadcast notification to views to allow end of group processing.
// NotifyGroupCompleted may throw (for memory exhaustion) but this method
// may not as it is called in UndoGroup destructor so ignore exception.
try {
NotifyGroupCompleted();
} catch (...) {
// Ignore any exception
}
}
}
int Document::UndoSequenceDepth() const noexcept {
return cb.UndoSequenceDepth();
}
@ -2513,6 +2527,25 @@ void Document::SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noex
pli = std::move(pLexInterface);
}
void Document::SetViewState(void *view, ViewStateShared pVSS) {
viewData[view] = pVSS;
}
ViewStateShared Document::GetViewState(void *view) const noexcept {
const std::map<void *, ViewStateShared>::const_iterator it = viewData.find(view);
if (it != viewData.end()) {
return it->second;
}
return {};
}
void Document::TruncateUndoComments(int action) {
for (auto &[key, value] : viewData) {
value->TruncateUndo(action);
}
}
int SCI_METHOD Document::SetLineState(Sci_Position line, int state) {
const int statePrevious = States()->SetLineState(line, state, LinesTotal());
if (state != statePrevious) {
@ -2709,6 +2742,12 @@ void Document::NotifySavePoint(bool atSavePoint) {
}
}
void Document::NotifyGroupCompleted() noexcept {
for (const WatcherWithUserData &watcher : watchers) {
watcher.watcher->NotifyGroupCompleted(this, watcher.userData);
}
}
void Document::NotifyModified(DocModification mh) {
if (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {
decorations->InsertSpace(mh.position, mh.length);
@ -2855,39 +2894,33 @@ Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxRe
const unsigned char chBrace = CharAt(position);
const unsigned char chSeek = BraceOpposite(chBrace);
if (chSeek == '\0')
return - 1;
return -1;
const int styBrace = StyleIndexAt(position);
int direction = -1;
if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
direction = 1;
int depth = 1;
position = useStartPos ? startPos : NextPosition(position, direction);
position = useStartPos ? startPos : position + direction;
// Avoid complex NextPosition call for bytes that may not cause incorrect match
// Avoid using MovePositionOutsideChar to check DBCS trail byte
unsigned char maxSafeChar = 0xff;
if (dbcsCodePage != 0 && dbcsCodePage != CpUtf8) {
maxSafeChar = (direction >= 0) ? 0x80 : DBCSMinTrailByte() - 1;
maxSafeChar = DBCSMinTrailByte() - 1;
}
while ((position >= 0) && (position < LengthNoExcept())) {
const unsigned char chAtPos = CharAt(position);
if (chAtPos == chBrace || chAtPos == chSeek) {
if ((position > GetEndStyled()) || (StyleIndexAt(position) == styBrace)) {
if (((position > GetEndStyled()) || (StyleIndexAt(position) == styBrace)) &&
(chAtPos <= maxSafeChar || position == MovePositionOutsideChar(position, direction, false))) {
depth += (chAtPos == chBrace) ? 1 : -1;
if (depth == 0)
return position;
}
}
if (chAtPos <= maxSafeChar) {
position += direction;
} else {
const Sci::Position positionBeforeMove = position;
position = NextPosition(position, direction);
if (position == positionBeforeMove)
break;
}
position += direction;
}
return - 1;
return -1;
}
/**
@ -2915,14 +2948,13 @@ namespace {
*/
class RESearchRange {
public:
const Document *doc;
int increment;
Sci::Position startPos;
Sci::Position endPos;
Sci::Line lineRangeStart;
Sci::Line lineRangeEnd;
Sci::Line lineRangeBreak;
RESearchRange(const Document *doc_, Sci::Position minPos, Sci::Position maxPos) noexcept : doc(doc_) {
RESearchRange(const Document *doc, Sci::Position minPos, Sci::Position maxPos) noexcept {
increment = (minPos <= maxPos) ? 1 : -1;
// Range endpoints should not be inside DBCS characters or between a CR and LF,

View File

@ -9,6 +9,10 @@
#define DOCUMENT_H
// >>>>>>>>>>>>>>> BEG NON STD SCI PATCH >>>>>>>>>>>>>>>
#if defined(__cplusplus)
#include <map>
#else
#endif
#include "ILoader.h"
// <<<<<<<<<<<<<<< END NON STD SCI PATCH <<<<<<<<<<<<<<<
@ -176,6 +180,22 @@ public:
bool isEnabled;
};
// Base class for view state that can be held and transferred without understanding the contents.
// Declared here but real implementation subclass declared in EditModel
struct ViewState {
ViewState() noexcept = default;
// Deleted so ViewState objects can not be copied
ViewState(const ViewState &) = delete;
ViewState(ViewState &&) = delete;
ViewState &operator=(const ViewState &) = delete;
ViewState &operator=(ViewState &&) = delete;
virtual ~ViewState() noexcept = default;
virtual void TruncateUndo(int index)=0;
};
using ViewStateShared = std::shared_ptr<ViewState>;
struct LexerReleaser {
// Called by unique_ptr to destroy/free the Resource
void operator()(Scintilla::ILexer5 *pLexer) noexcept {
@ -308,6 +328,8 @@ private:
std::unique_ptr<RegexSearchBase> regex;
std::unique_ptr<LexInterface> pli;
std::map<void *, ViewStateShared>viewData;
public:
Scintilla::EndOfLine eolMode;
@ -400,8 +422,9 @@ public:
}
bool IsCollectingUndo() const noexcept { return cb.IsCollectingUndo(); }
void BeginUndoAction(bool coalesceWithPrior=false) noexcept { cb.BeginUndoAction(coalesceWithPrior); }
void EndUndoAction() noexcept { cb.EndUndoAction(); }
void EndUndoAction() noexcept;
int UndoSequenceDepth() const noexcept;
bool AfterUndoSequenceStart() const noexcept { return cb.AfterUndoSequenceStart(); }
void AddUndoAction(Sci::Position token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); }
void SetSavePoint();
bool IsSavePoint() const noexcept { return cb.IsSavePoint(); }
@ -538,6 +561,10 @@ public:
LexInterface *GetLexInterface() const noexcept;
void SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noexcept;
void SetViewState(void *view, ViewStateShared pVSS);
ViewStateShared GetViewState(void *view) const noexcept;
void TruncateUndoComments(int action);
int SCI_METHOD SetLineState(Sci_Position line, int state) override;
int SCI_METHOD GetLineState(Sci_Position line) const noexcept override;
Sci::Line GetMaxLineState() const noexcept;
@ -578,6 +605,7 @@ public:
private:
void NotifyModifyAttempt();
void NotifySavePoint(bool atSavePoint);
void NotifyGroupCompleted() noexcept;
void NotifyModified(DocModification mh);
};
@ -668,6 +696,7 @@ public:
virtual void NotifyDeleted(Document *doc, void *userData) noexcept = 0;
virtual void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endPos) = 0;
virtual void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) = 0;
virtual void NotifyGroupCompleted(Document *doc, void *userData) noexcept = 0;
};
}

View File

@ -58,6 +58,54 @@ using namespace Scintilla::Internal;
Caret::Caret() noexcept :
active(false), on(false), period(500) {}
SelectionSimple::SelectionSimple(const Selection &sel) {
selType = sel.selType;
if (sel.IsRectangular()) {
// rectangular or thin
// Could be large so don't remember each range, just the rectangular bounds then reconstitute when undone
rangeRectangular = sel.RectangularCopy();
} else {
ranges = sel.RangesCopy();
}
mainRange = sel.Main();
}
void ModelState::RememberSelectionForUndo(int index, const Selection &sel) {
historyForUndo.indexCurrent = index;
historyForUndo.ssCurrent = SelectionSimple(sel);
}
void ModelState::ForgetSelectionForUndo() noexcept {
historyForUndo.indexCurrent = -1;
}
void ModelState::RememberSelectionOntoStack(int index) {
if ((historyForUndo.indexCurrent >= 0) && (index == historyForUndo.indexCurrent + 1)) {
// Don't overwrite initial selection save if most recent action was coalesced
historyForUndo.stack[index] = historyForUndo.ssCurrent;
}
}
void ModelState::RememberSelectionForRedoOntoStack(int index, const Selection &sel) {
historyForRedo.stack[index] = SelectionSimple(sel);
}
const SelectionSimple *ModelState::SelectionFromStack(int index, UndoRedo history) const {
const SelectionHistory &sh = history == UndoRedo::undo ? historyForUndo : historyForRedo;
std::map<int, SelectionSimple>::const_iterator it = sh.stack.find(index);
if (it != sh.stack.end()) {
return &it->second;
}
return {};
}
void ModelState::TruncateUndo(int index) {
std::map<int, SelectionSimple>::iterator itUndo = historyForUndo.stack.find(index);
historyForUndo.stack.erase(itUndo, historyForUndo.stack.end());
std::map<int, SelectionSimple>::iterator itRedo = historyForRedo.stack.find(index);
historyForRedo.stack.erase(itRedo, historyForRedo.stack.end());
}
EditModel::EditModel() : braces{} {
inOverstrike = false;
xOffset = 0;
@ -135,3 +183,14 @@ InSelection EditModel::LineEndInSelection(Sci::Line lineDoc) const {
int EditModel::GetMark(Sci::Line line) const {
return pdoc->GetMark(line, FlagSet(changeHistoryOption, ChangeHistoryOption::Markers));
}
void EditModel::EnsureModelState() {
if (!modelState && (undoSelectionHistoryOption == UndoSelectionHistoryOption::Enabled)) {
if (ViewStateShared vss = pdoc->GetViewState(this)) {
modelState = std::dynamic_pointer_cast<ModelState>(vss);
} else {
modelState = std::make_shared<ModelState>();
pdoc->SetViewState(this, std::static_pointer_cast<ViewState>(modelState));
}
}
}

View File

@ -21,6 +21,41 @@ public:
Caret() noexcept;
};
// Simplified version of selection which won't contain rectangular selection realized
// into ranges as too much data.
// Just a type and single range for now.
struct SelectionSimple {
std::vector<SelectionRange> ranges;
SelectionRange rangeRectangular;
size_t mainRange = 0;
Selection::SelTypes selType = Selection::SelTypes::stream;
SelectionSimple() = default;
explicit SelectionSimple(const Selection &sel);
};
enum class UndoRedo { undo, redo };
struct SelectionHistory {
int indexCurrent = 0;
SelectionSimple ssCurrent;
std::map<int, SelectionSimple> stack;
};
struct ModelState : ViewState {
SelectionHistory historyForUndo;
SelectionHistory historyForRedo;
void RememberSelectionForUndo(int index, const Selection &sel);
void ForgetSelectionForUndo() noexcept;
void RememberSelectionOntoStack(int index);
void RememberSelectionForRedoOntoStack(int index, const Selection &sel);
const SelectionSimple *SelectionFromStack(int index, UndoRedo history) const;
virtual void TruncateUndo(int index) final;
};
using ModelStateShared = std::shared_ptr<ModelState>;
class EditModel {
public:
bool inOverstrike;
@ -61,6 +96,10 @@ public:
Document *pdoc;
Scintilla::UndoSelectionHistoryOption undoSelectionHistoryOption = UndoSelectionHistoryOption::Disabled;
bool needRedoRemembered = false;
ModelStateShared modelState;
EditModel();
// Deleted so EditModel objects can not be copied.
EditModel(const EditModel &) = delete;
@ -79,6 +118,8 @@ public:
const char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept;
InSelection LineEndInSelection(Sci::Line lineDoc) const;
[[nodiscard]] int GetMark(Sci::Line line) const;
void EnsureModelState();
};
}

View File

@ -2541,19 +2541,13 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
Sci::Line lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
std::shared_ptr<LineLayout> ll;
std::vector<DrawPhase> phases;
DrawPhase phase = DrawPhase::all;
if ((phasesDraw == PhasesDraw::Multiple) && !bufferedDraw) {
for (DrawPhase phase = DrawPhase::back; phase <= DrawPhase::carets; phase = static_cast<DrawPhase>(static_cast<int>(phase) * 2)) {
phases.push_back(phase);
}
} else {
phases.push_back(DrawPhase::all);
phase = DrawPhase::back;
}
for (const DrawPhase &phase : phases) {
int ypos = 0;
if (!bufferedDraw)
ypos += screenLinePaintFirst * vsDraw.lineHeight;
for (;;) {
int yposScreen = screenLinePaintFirst * vsDraw.lineHeight;
int ypos = bufferedDraw ? 0 : yposScreen;
Sci::Line visibleLine = model.TopLineOfMain() + screenLinePaintFirst;
while (visibleLine < model.pcs->LinesDisplayed() && yposScreen < rcArea.bottom) {
@ -2572,6 +2566,10 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
ll = RetrieveLineLayout(lineDoc, model);
LayoutLine(model, surface, vsDraw, ll.get(), model.wrapWidth);
lineDocPrevious = lineDoc;
if (ll && model.BidirectionalEnabled()) {
// Fill the line bidi data
UpdateBidiData(model, vsDraw, ll.get());
}
}
#if defined(TIME_PAINTING)
durLayout += ep.Duration(true);
@ -2599,11 +2597,6 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
surface->FillRectangleAligned(rcSpacer, Fill(vsDraw.styles[StyleDefault].back));
}
if (model.BidirectionalEnabled()) {
// Fill the line bidi data
UpdateBidiData(model, vsDraw, ll.get());
}
DrawLine(surface, model, vsDraw, ll.get(), lineDoc, visibleLine, xStart, rcLine, subLine, phase);
#if defined(TIME_PAINTING)
durPaint += ep.Duration(true);
@ -2642,6 +2635,11 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
yposScreen += vsDraw.lineHeight;
visibleLine++;
}
if (phase >= DrawPhase::carets) {
break;
}
phase = static_cast<DrawPhase>(static_cast<int>(phase) * 2);
}
ll.reset();
#if defined(TIME_PAINTING)

View File

@ -517,10 +517,14 @@ void Editor::RedrawSelMargin(Sci::Line line, bool allAfter) {
}
PRectangle Editor::RectangleFromRange(Range r, int overlap) {
const Sci::Line minLine = pcs->DisplayFromDoc(
pdoc->SciLineFromPosition(r.First()));
const Sci::Line maxLine = pcs->DisplayLastFromDoc(
pdoc->SciLineFromPosition(r.Last()));
const Sci::Line docLineFirst = pdoc->SciLineFromPosition(r.First());
const Sci::Line minLine = pcs->DisplayFromDoc(docLineFirst);
Sci::Line docLineLast = docLineFirst; // Common case where range is wholly in one document line
if (r.Last() >= pdoc->LineStart(docLineFirst + 1)) {
// Range covers multiple lines so need last line
docLineLast = pdoc->SciLineFromPosition(r.Last());
}
const Sci::Line maxLine = pcs->DisplayLastFromDoc(docLineLast);
const PRectangle rcClientDrawing = GetClientDrawingRectangle();
PRectangle rc;
const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
@ -923,6 +927,37 @@ void Editor::SetLastXChosen() {
lastXChosen = static_cast<int>(pt.x) + xOffset;
}
void Editor::RememberSelectionForUndo(int index) {
EnsureModelState();
if (modelState) {
modelState->RememberSelectionForUndo(index, sel);
needRedoRemembered = true;
// Remember selection at end of processing current message
}
}
void Editor::RememberSelectionOntoStack(int index) {
EnsureModelState();
if (modelState) {
// Is undo currently inside a group?
if (!pdoc->AfterUndoSequenceStart()) {
// Don't remember selections inside a grouped sequence as can only
// unto or redo to the start and end of the group.
modelState->RememberSelectionOntoStack(index);
}
}
}
void Editor::RememberCurrentSelectionForRedoOntoStack() {
if (needRedoRemembered && (pdoc->UndoSequenceDepth() == 0)) {
EnsureModelState();
if (modelState) {
modelState->RememberSelectionForRedoOntoStack(pdoc->UndoCurrent(), sel);
needRedoRemembered = false;
}
}
}
void Editor::ScrollTo(Sci::Line line, bool moveThumb) {
const Sci::Line topLineNew = std::clamp<Sci::Line>(line, 0, MaxScrollPos());
if (topLineNew != topLine) {
@ -1000,21 +1035,25 @@ void Editor::MoveSelectedLines(int lineDelta) {
// if selection doesn't end at the beginning of a line greater than that of the start,
// then set it at the beginning of the next one
Sci::Position selectionEnd = SelectionEnd().Position();
const Sci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);
Sci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);
const Sci::Position beginningOfEndLine = pdoc->LineStart(endLine);
bool appendEol = false;
if (selectionEnd > beginningOfEndLine
|| selectionStart == selectionEnd) {
selectionEnd = pdoc->LineStart(endLine + 1);
appendEol = (selectionEnd == pdoc->Length() && pdoc->SciLineFromPosition(selectionEnd) == endLine);
endLine = pdoc->SciLineFromPosition(selectionEnd);
}
// if there's nowhere for the selection to move
// (i.e. at the beginning going up or at the end going down),
// stop it right there!
const bool docEndLineEmpty = pdoc->LineStart(endLine) == pdoc->Length();
if ((selectionStart == 0 && lineDelta < 0)
|| (selectionEnd == pdoc->Length() && lineDelta > 0)
|| selectionStart == selectionEnd) {
|| (selectionEnd == pdoc->Length() && lineDelta > 0
&& !docEndLineEmpty) // allow moving when end line of document is empty
|| ((selectionStart == selectionEnd)
&& !(lineDelta < 0 && docEndLineEmpty && selectionEnd == pdoc->Length()))) { // allow moving-up last empty line
return;
}
@ -2080,13 +2119,13 @@ void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) {
}
}
}
ThinRectangularRange();
}
if (wrapOccurred) {
SetScrollBars();
SetVerticalScrollPos();
Redraw();
}
ThinRectangularRange();
// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
EnsureCaretVisible();
// Avoid blinking during rapid typing:
@ -2362,22 +2401,44 @@ void Editor::SelectAll() {
Redraw();
}
void Editor::RestoreSelection(Sci::Position newPos, UndoRedo history) {
EnsureModelState();
if ((undoSelectionHistoryOption == UndoSelectionHistoryOption::Enabled) && modelState) {
// Undo wants the element after the current as it just undid it
const int index = pdoc->UndoCurrent() + (history == UndoRedo::undo ? 1 : 0);
const SelectionSimple *pss = modelState->SelectionFromStack(index, history);
if (pss) {
sel.selType = pss->selType;
if (sel.IsRectangular()) {
sel.Rectangular() = pss->rangeRectangular;
// Reconstitute ranges from rectangular range
SetRectangularRange();
} else {
sel.SetRanges(pss->ranges);
}
// Unsure if this is safe with SetMain potentially failing if document doesn't appear the same.
// Maybe this can occur if the user changes font or wrap mode?
sel.SetMain(pss->mainRange);
newPos = -1; // Used selection from stack so don't use position returned from undo/redo.
}
}
if (newPos >= 0)
SetEmptySelection(newPos);
EnsureCaretVisible();
}
void Editor::Undo() {
if (pdoc->CanUndo()) {
InvalidateCaret();
const Sci::Position newPos = pdoc->Undo();
if (newPos >= 0)
SetEmptySelection(newPos);
EnsureCaretVisible();
RestoreSelection(newPos, UndoRedo::undo);
}
}
void Editor::Redo() {
if (pdoc->CanRedo()) {
const Sci::Position newPos = pdoc->Redo();
if (newPos >= 0)
SetEmptySelection(newPos);
EnsureCaretVisible();
RestoreSelection(newPos, UndoRedo::redo);
}
}
@ -2453,6 +2514,17 @@ void Editor::NotifyErrorOccurred(Document *, void *, Status status) {
errorStatus = status;
}
void Editor::NotifyGroupCompleted(Document *, void *) noexcept {
// RememberCurrentSelectionForRedoOntoStack may throw (for memory exhaustion)
// but this method may not as it is called in UndoGroup destructor so ignore
// exception.
try {
RememberCurrentSelectionForRedoOntoStack();
} catch (...) {
// Ignore any exception
}
}
void Editor::NotifyChar(int ch, CharacterSource charSource) {
NotificationData scn = {};
scn.nmhdr.code = Notification::CharAdded;
@ -2721,6 +2793,15 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) {
view.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);
}
} else {
if ((undoSelectionHistoryOption == UndoSelectionHistoryOption::Enabled) &&
FlagSet(mh.modificationType, ModificationFlags::User)) {
if (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete)) {
RememberSelectionForUndo(pdoc->UndoCurrent());
}
if (FlagSet(mh.modificationType, ModificationFlags::InsertText | ModificationFlags::DeleteText)) {
RememberSelectionOntoStack(pdoc->UndoCurrent());
}
}
// Move selection and brace highlights
if (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {
sel.MovePositions(true, mh.position, mh.length);
@ -5473,6 +5554,7 @@ void Editor::SetDocPointer(Document *document) {
pdoc = document;
}
pdoc->AddRef();
modelState.reset();
pcs = ContractionStateCreate(pdoc->IsLarge());
// Ensure all positions within document
@ -8661,6 +8743,13 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case Message::GetChangeHistory:
return static_cast<sptr_t>(changeHistoryOption);
case Message::SetUndoSelectionHistory:
undoSelectionHistoryOption = static_cast<UndoSelectionHistoryOption>(wParam);
break;
case Message::GetUndoSelectionHistory:
return static_cast<sptr_t>(undoSelectionHistoryOption);
case Message::SetExtraAscent:
vs.extraAscent = static_cast<int>(wParam);
InvalidateStyleRedraw();
@ -9070,6 +9159,11 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
default:
return DefWndProc(iMessage, wParam, lParam);
}
// If there was a change that needs its selection saved and it wasn't explicity saved
// then do that here.
RememberCurrentSelectionForRedoOntoStack();
//Platform::DebugPrintf("end wnd proc\n");
return 0;
}

View File

@ -182,6 +182,8 @@ constexpr XYScrollOptions operator|(XYScrollOptions a, XYScrollOptions b) noexce
return static_cast<XYScrollOptions>(static_cast<int>(a) | static_cast<int>(b));
}
struct SelectionStack;
/**
*/
class Editor : public EditModel, public DocWatcher {
@ -371,6 +373,9 @@ protected: // ScintillaBase subclass needs access to much of Editor
SelectionPosition MovePositionSoVisible(Sci::Position pos, int moveDir);
Point PointMainCaret();
void SetLastXChosen();
void RememberSelectionForUndo(int index);
void RememberSelectionOntoStack(int index);
void RememberCurrentSelectionForRedoOntoStack();
void ScrollTo(Sci::Line line, bool moveThumb=true);
virtual void ScrollText(Sci::Line linesToMove);
@ -447,6 +452,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
virtual void Paste() = 0;
void Clear();
virtual void SelectAll();
void RestoreSelection(Sci::Position newPos, UndoRedo history);
virtual void Undo();
virtual void Redo();
void DelCharBack(bool allowLineStartDeletion);
@ -483,6 +489,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
void NotifyDeleted(Document *document, void *userData) noexcept override;
void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endStyleNeeded) override;
void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) override;
void NotifyGroupCompleted(Document *, void *) noexcept override;
void NotifyMacroRecord(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);
void ContainerNeedsUpdate(Scintilla::Update flags) noexcept;

View File

@ -185,7 +185,7 @@ public:
T upper = Partitions();
do {
const T middle = (upper + lower + 1) / 2; // Round high
T posMiddle = body.ValueAt(middle);
T posMiddle = body[middle];
if (middle > stepPartition)
posMiddle += stepLength;
if (pos < posMiddle) {

View File

@ -80,8 +80,6 @@ void RunStyles<DISTANCE, STYLE>::RemoveRunIfSameAsPrevious(DISTANCE run) {
template <typename DISTANCE, typename STYLE>
RunStyles<DISTANCE, STYLE>::RunStyles() {
starts = Partitioning<DISTANCE>(8);
styles = SplitVector<STYLE>();
styles.InsertValue(0, 2, 0);
}
@ -136,7 +134,8 @@ FillResult<DISTANCE> RunStyles<DISTANCE, STYLE>::FillRange(DISTANCE position, ST
return resultNoChange;
}
DISTANCE runEnd = RunFromPosition(end);
if (styles.ValueAt(runEnd) == value) {
const STYLE valueCurrent = styles.ValueAt(runEnd);
if (valueCurrent == value) {
// End already has value so trim range.
end = starts.PositionFromPartition(runEnd);
if (position >= end) {
@ -145,6 +144,22 @@ FillResult<DISTANCE> RunStyles<DISTANCE, STYLE>::FillRange(DISTANCE position, ST
}
fillLength = end - position;
} else {
const DISTANCE startRun = starts.PositionFromPartition(runEnd);
if (position > startRun) {
const DISTANCE runNext = runEnd + 1;
const DISTANCE endRun = starts.PositionFromPartition(runNext);
if (end < endRun) {
// New piece is completely inside a run with a different value so its a simple
// insertion of two points [ (position, value), (end, valueCurrent) ]
const DISTANCE range[] { position, end};
starts.InsertPartitions(runEnd + 1, range, 2);
// Temporary runEndIndex silences non-useful arithmetic overflow warnings
const ptrdiff_t runEndIndex = runEnd;
styles.Insert(runEndIndex + 1, value);
styles.Insert(runEndIndex + 2, valueCurrent);
return { true, position, fillLength };
}
}
runEnd = SplitRun(end);
}
DISTANCE runStart = RunFromPosition(position);
@ -172,9 +187,8 @@ FillResult<DISTANCE> RunStyles<DISTANCE, STYLE>::FillRange(DISTANCE position, ST
runEnd = RunFromPosition(end);
RemoveRunIfEmpty(runEnd);
return result;
} else {
return resultNoChange;
}
return resultNoChange;
}
template <typename DISTANCE, typename STYLE>
@ -213,7 +227,7 @@ void RunStyles<DISTANCE, STYLE>::InsertSpace(DISTANCE position, DISTANCE insertL
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::DeleteAll() {
starts = Partitioning<DISTANCE>(8);
starts = Partitioning<DISTANCE>();
styles = SplitVector<STYLE>();
styles.InsertValue(0, 2, 0);
}

View File

@ -52,13 +52,6 @@ void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startC
}
}
bool SelectionPosition::operator <(const SelectionPosition &other) const noexcept {
if (position == other.position)
return virtualSpace < other.virtualSpace;
else
return position < other.position;
}
bool SelectionPosition::operator >(const SelectionPosition &other) const noexcept {
if (position == other.position)
return virtualSpace > other.virtualSpace;
@ -217,6 +210,10 @@ 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].anchor, ranges[0].caret);
@ -428,11 +425,13 @@ void Selection::Clear() noexcept {
if (ranges.size() > 1) {
ranges.erase(ranges.begin() + 1, ranges.end());
}
mainRange = 0;
selType = SelTypes::stream;
moveExtends = false;
ranges[mainRange].Reset();
ranges[0].Reset();
rangesSaved.clear();
rangeRectangular.Reset();
mainRange = 0;
moveExtends = false;
tentativeMain = false;
selType = SelTypes::stream;
}
void Selection::RemoveDuplicates() noexcept {
@ -456,3 +455,6 @@ void Selection::RotateMain() noexcept {
mainRange = (mainRange + 1) % ranges.size();
}
void Selection::SetRanges(const Ranges &rangesToSet) {
ranges = rangesToSet;
}

View File

@ -11,10 +11,11 @@
namespace Scintilla::Internal {
class SelectionPosition {
Sci::Position position;
Sci::Position virtualSpace;
Sci::Position position = Sci::invalidPosition;
Sci::Position virtualSpace = 0;
public:
explicit SelectionPosition(Sci::Position position_= Sci::invalidPosition, Sci::Position virtualSpace_=0) noexcept : position(position_), virtualSpace(virtualSpace_) {
constexpr SelectionPosition() noexcept = default;
constexpr explicit SelectionPosition(Sci::Position position_, Sci::Position virtualSpace_=0) noexcept : position(position_), virtualSpace(virtualSpace_) {
PLATFORM_ASSERT(virtualSpace < 800000000);
if (virtualSpace < 0)
virtualSpace = 0;
@ -24,13 +25,20 @@ public:
virtualSpace = 0;
}
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;
[[nodiscard]] constexpr bool operator ==(const SelectionPosition &other) const noexcept {
return (position == other.position) && (virtualSpace == other.virtualSpace);
}
bool operator <(const SelectionPosition &other) const noexcept;
bool operator >(const SelectionPosition &other) const noexcept;
bool operator <=(const SelectionPosition &other) const noexcept;
bool operator >=(const SelectionPosition &other) const noexcept;
[[nodiscard]] constexpr bool operator !=(const SelectionPosition &other) const noexcept {
return (position != other.position) || (virtualSpace != other.virtualSpace);
}
[[nodiscard]] constexpr bool operator <(const SelectionPosition &other) const noexcept {
if (position == other.position)
return virtualSpace < other.virtualSpace;
return position < other.position;
}
[[nodiscard]] bool operator >(const SelectionPosition &other) const noexcept;
[[nodiscard]] bool operator <=(const SelectionPosition &other) const noexcept;
[[nodiscard]] bool operator >=(const SelectionPosition &other) const noexcept;
Sci::Position Position() const noexcept {
return position;
}
@ -61,9 +69,8 @@ public:
struct SelectionSegment {
SelectionPosition start;
SelectionPosition end;
SelectionSegment() noexcept : start(), end() {
}
SelectionSegment(SelectionPosition a, SelectionPosition b) noexcept {
constexpr SelectionSegment() noexcept = default;
constexpr SelectionSegment(SelectionPosition a, SelectionPosition b) noexcept {
if (a < b) {
start = a;
end = b;
@ -96,15 +103,14 @@ struct SelectionRange {
SelectionPosition caret;
SelectionPosition anchor;
SelectionRange() noexcept : caret(), anchor() {
constexpr SelectionRange() noexcept = default;
constexpr explicit SelectionRange(SelectionPosition single) noexcept : caret(single), anchor(single) {
}
explicit SelectionRange(SelectionPosition single) noexcept : caret(single), anchor(single) {
constexpr explicit SelectionRange(Sci::Position single) noexcept : caret(single), anchor(single) {
}
explicit SelectionRange(Sci::Position single) noexcept : caret(single), anchor(single) {
constexpr SelectionRange(SelectionPosition caret_, SelectionPosition anchor_) noexcept : caret(caret_), anchor(anchor_) {
}
SelectionRange(SelectionPosition caret_, SelectionPosition anchor_) noexcept : caret(caret_), anchor(anchor_) {
}
SelectionRange(Sci::Position caret_, Sci::Position anchor_) noexcept : caret(caret_), anchor(anchor_) {
constexpr SelectionRange(Sci::Position caret_, Sci::Position anchor_) noexcept : caret(caret_), anchor(anchor_) {
}
bool Empty() const noexcept {
return anchor == caret;
@ -147,8 +153,9 @@ struct SelectionRange {
enum InSelection { inNone, inMain, inAdditional };
class Selection {
std::vector<SelectionRange> ranges;
std::vector<SelectionRange> rangesSaved;
using Ranges = std::vector<SelectionRange>;
Ranges ranges;
Ranges rangesSaved;
SelectionRange rangeRectangular;
size_t mainRange;
bool moveExtends;
@ -157,11 +164,12 @@ public:
enum class SelTypes { none, stream, rectangle, lines, thin };
SelTypes selType;
Selection();
Selection(); // Allocates so may throw.
bool IsRectangular() const noexcept;
Sci::Position MainCaret() const noexcept;
Sci::Position MainAnchor() const noexcept;
SelectionRange &Rectangular() noexcept;
SelectionRange RectangularCopy() const noexcept;
SelectionSegment Limits() const noexcept;
// This is for when you want to move the caret in response to a
// user direction command - for rectangular selections, use the range
@ -198,9 +206,10 @@ public:
void RemoveDuplicates() noexcept;
void RotateMain() noexcept;
bool Tentative() const noexcept { return tentativeMain; }
std::vector<SelectionRange> RangesCopy() const {
Ranges RangesCopy() const {
return ranges;
}
void SetRanges(const Ranges &rangesToSet);
};
}

View File

@ -27,8 +27,6 @@ private:
}
public:
SparseVector() : empty() {
starts = Partitioning<Sci::Position>(8);
values = SplitVector<T>();
values.InsertEmpty(0, 2);
}
Sci::Position Length() const noexcept {
@ -158,7 +156,7 @@ public:
Check();
}
void DeleteAll() {
starts = Partitioning<Sci::Position>(8);
starts = Partitioning<Sci::Position>();
values = SplitVector<T>();
values.InsertEmpty(0, 2);
}

View File

@ -195,7 +195,7 @@ public:
}
RoomFor(insertLength);
GapTo(position);
std::fill(body.data() + part1Length, body.data() + part1Length + insertLength, v);
std::fill_n(body.data() + part1Length, insertLength, v);
lengthBody += insertLength;
part1Length += insertLength;
gapLength -= insertLength;
@ -214,10 +214,8 @@ public:
}
RoomFor(insertLength);
GapTo(position);
for (ptrdiff_t elem = part1Length; elem < part1Length + insertLength; elem++) {
T emptyOne = {};
body[elem] = std::move(emptyOne);
}
T *ptr = body.data() + part1Length;
std::uninitialized_value_construct_n(ptr, insertLength);
lengthBody += insertLength;
part1Length += insertLength;
gapLength -= insertLength;
@ -242,7 +240,7 @@ public:
}
RoomFor(insertLength);
GapTo(positionToInsert);
std::copy(s + positionFrom, s + positionFrom + insertLength, body.data() + part1Length);
std::copy_n(s + positionFrom, insertLength, body.data() + part1Length);
lengthBody += insertLength;
part1Length += insertLength;
gapLength -= insertLength;
@ -288,11 +286,11 @@ public:
if (range1Length > part1AfterPosition)
range1Length = part1AfterPosition;
}
std::copy(body.data() + position, body.data() + position + range1Length, buffer);
std::copy_n(body.data() + position, range1Length, buffer);
buffer += range1Length;
position = position + range1Length + gapLength;
const ptrdiff_t range2Length = retrieveLength - range1Length;
std::copy(body.data() + position, body.data() + position + range2Length, buffer);
std::copy_n(body.data() + position, range2Length, buffer);
}
/// Compact the buffer and return a pointer to the first element.

View File

@ -354,6 +354,14 @@ int UndoHistory::UndoSequenceDepth() const noexcept {
return undoSequenceDepth;
}
bool UndoHistory::AfterUndoSequenceStart() const noexcept {
if (currentAction == 0) {
return false;
}
// Count back to last sequence start?
return !actions.AtStart(currentAction-1);
}
void UndoHistory::DropUndoSequence() noexcept {
undoSequenceDepth = 0;
}

View File

@ -102,6 +102,7 @@ public:
void BeginUndoAction(bool mayCoalesce=false) noexcept;
void EndUndoAction() noexcept;
int UndoSequenceDepth() const noexcept;
bool AfterUndoSequenceStart() const noexcept;
void DropUndoSequence() noexcept;
void DeleteUndoHistory() noexcept;

View File

@ -1 +1 @@
553
554

View File

@ -6,8 +6,8 @@
#include <windows.h>
#define VERSION_SCINTILLA "5.5.3"
#define VERSION_WORDS 5, 5, 3, 0
#define VERSION_SCINTILLA "5.5.4"
#define VERSION_WORDS 5, 5, 4, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS

View File

@ -272,7 +272,7 @@ public:
return ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);
}
std::vector<BYTE> GetImeAttributes() {
std::vector<BYTE> GetImeAttributes() const {
const int attrLen = ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, nullptr, 0);
std::vector<BYTE> attr(attrLen, 0);
::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, &attr[0], static_cast<DWORD>(attr.size()));
@ -284,7 +284,7 @@ public:
return byteLen / sizeof(wchar_t);
}
std::wstring GetCompositionString(DWORD dwIndex) {
std::wstring GetCompositionString(DWORD dwIndex) const {
const LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, nullptr, 0);
std::wstring wcs(byteLen / 2, 0);
::ImmGetCompositionStringW(hIMC, dwIndex, &wcs[0], byteLen);