aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2022-11-22 09:24:07 +1100
committerNeil <nyamatongwe@gmail.com>2022-11-22 09:24:07 +1100
commitcb8cd73d839a96f98bb1ce887c694271f9c24788 (patch)
tree1920fae848da38e7942ea9a36c21f87272254e23
parentb3e46461ce564d295b629a1d16dc4cee60722e66 (diff)
downloadscintilla-mirror-cb8cd73d839a96f98bb1ce887c694271f9c24788.tar.gz
Add SCI_REPLACETARGETMINIMAL to change text without causing unchanged prefix and
suffix to be marked as modified in change history.
-rw-r--r--call/ScintillaCall.cxx8
-rw-r--r--doc/ScintillaDoc.html25
-rw-r--r--doc/ScintillaHistory.html4
-rw-r--r--include/Scintilla.h1
-rw-r--r--include/Scintilla.iface4
-rw-r--r--include/ScintillaCall.h2
-rw-r--r--include/ScintillaMessages.h1
-rw-r--r--src/Document.cxx11
-rw-r--r--src/Document.h5
-rw-r--r--src/Editor.cxx36
-rw-r--r--src/Editor.h9
-rw-r--r--test/simpleTests.py39
12 files changed, 128 insertions, 17 deletions
diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx
index 816f25eb9..27ad8a738 100644
--- a/call/ScintillaCall.cxx
+++ b/call/ScintillaCall.cxx
@@ -130,6 +130,10 @@ Position ScintillaCall::ReplaceTargetRE(std::string_view text) {
return CallString(Message::ReplaceTargetRE, text.length(), text.data());
}
+Position ScintillaCall::ReplaceTargetMinimal(std::string_view text) {
+ return CallString(Message::ReplaceTargetMinimal, text.length(), text.data());
+}
+
Position ScintillaCall::SearchInTarget(std::string_view text) {
return CallString(Message::SearchInTarget, text.length(), text.data());
}
@@ -1419,6 +1423,10 @@ Position ScintillaCall::ReplaceTargetRE(Position length, const char *text) {
return CallString(Message::ReplaceTargetRE, length, text);
}
+Position ScintillaCall::ReplaceTargetMinimal(Position length, const char *text) {
+ return CallString(Message::ReplaceTargetMinimal, length, text);
+}
+
Position ScintillaCall::SearchInTarget(Position length, const char *text) {
return CallString(Message::SearchInTarget, length, text);
}
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html
index cab6e397c..8fe205014 100644
--- a/doc/ScintillaDoc.html
+++ b/doc/ScintillaDoc.html
@@ -129,7 +129,7 @@
<h1>Scintilla Documentation</h1>
- <p>Last edited 26 October 2022 NH</p>
+ <p>Last edited 15 November 2022 NH</p>
<p style="background:#90F0C0">Scintilla 5 has moved the lexers from Scintilla into a new
<a href="Lexilla.html">Lexilla</a> project.<br />
@@ -821,6 +821,7 @@ struct Sci_TextRangeFull {
<a class="message" href="#SCI_SEARCHINTARGET">SCI_SEARCHINTARGET(position length, const char *text) &rarr; position</a><br />
<a class="message" href="#SCI_GETTARGETTEXT">SCI_GETTARGETTEXT(&lt;unused&gt;, char *text) &rarr; position</a><br />
<a class="message" href="#SCI_REPLACETARGET">SCI_REPLACETARGET(position length, const char *text) &rarr; position</a><br />
+ <a class="message" href="#SCI_REPLACETARGETMINIMAL">SCI_REPLACETARGETMINIMAL(position length, const char *text) &rarr; position</a><br />
<a class="message" href="#SCI_REPLACETARGETRE">SCI_REPLACETARGETRE(position length, const char *text) &rarr; position</a><br />
<a class="message" href="#SCI_GETTAG">SCI_GETTAG(int tagNumber, char *tagValue) &rarr; int</a><br />
</code>
@@ -870,9 +871,21 @@ struct Sci_TextRangeFull {
<p><b id="SCI_REPLACETARGET">SCI_REPLACETARGET(position length, const char *text) &rarr; position</b><br />
If <code class="parameter">length</code> is -1, <code class="parameter">text</code> is a zero terminated string, otherwise
<code class="parameter">length</code> sets the number of character to replace the target with.
- After replacement, the target range refers to the replacement text.
- The return value
- is the length of the replacement string.<br />
+ After replacement, the target range refers to the replacement text.
+ The return value is the length of the replacement string.<br />
+ Note that the recommended way to delete text in the document is to set the target to the text to be removed,
+ and to perform a replace target with an empty string.</p>
+
+ <p><b id="SCI_REPLACETARGETMINIMAL">SCI_REPLACETARGETMINIMAL(position length, const char *text) &rarr; position</b><br />
+ This is similar to <a class="message" href="#SCI_REPLACETARGET"><code>SCI_REPLACETARGET</code></a>
+ but tries to minimize change history when the current target text shares a common prefix or suffix with the replacement.
+ Only the text that is actually different is marked as changed.
+ This might be used when automatically reformatting some text
+ so that the whole area formatted doesn't show change marks.
+ If <code class="parameter">length</code> is -1, <code class="parameter">text</code> is a zero terminated string, otherwise
+ <code class="parameter">length</code> sets the number of character to replace the target with.
+ After replacement, the target range refers to the replacement text.
+ The return value is the length of the replacement string.<br />
Note that the recommended way to delete text in the document is to set the target to the text to be removed,
and to perform a replace target with an empty string.</p>
@@ -882,8 +895,8 @@ struct Sci_TextRangeFull {
characters to use. The replacement string is formed from the text string with any sequences of
<code>\1</code> through <code>\9</code> replaced by tagged matches from the most recent regular
expression search. <code>\0</code> is replaced with all the matched text from the most recent search.
- After replacement, the target range refers to the replacement text.
- The return value is the length of the replacement string.</p>
+ After replacement, the target range refers to the replacement text.
+ The return value is the length of the replacement string.</p>
<p><b id="SCI_GETTAG">SCI_GETTAG(int tagNumber, char *tagValue NUL-terminated) &rarr; int</b><br />
Discover what text was matched by tagged expressions in a regular expression search.
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html
index 87303cb4f..d3787329c 100644
--- a/doc/ScintillaHistory.html
+++ b/doc/ScintillaHistory.html
@@ -585,6 +585,10 @@
Released 12 October 2022.
</li>
<li>
+ Add SCI_REPLACETARGETMINIMAL to change text without causing unchanged prefix and suffix
+ to be marked as modified in change history.
+ </li>
+ <li>
Draw background colour for EOL annotations with standard and boxed visuals.
</li>
<li>
diff --git a/include/Scintilla.h b/include/Scintilla.h
index 1baee5e49..43a41e986 100644
--- a/include/Scintilla.h
+++ b/include/Scintilla.h
@@ -557,6 +557,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SCI_TARGETWHOLEDOCUMENT 2690
#define SCI_REPLACETARGET 2194
#define SCI_REPLACETARGETRE 2195
+#define SCI_REPLACETARGETMINIMAL 2779
#define SCI_SEARCHINTARGET 2197
#define SCI_SETSEARCHFLAGS 2198
#define SCI_GETSEARCHFLAGS 2199
diff --git a/include/Scintilla.iface b/include/Scintilla.iface
index e65b5e5d7..9f02c78d2 100644
--- a/include/Scintilla.iface
+++ b/include/Scintilla.iface
@@ -1450,6 +1450,10 @@ fun position ReplaceTarget=2194(position length, string text)
# caused by processing the \d patterns.
fun position ReplaceTargetRE=2195(position length, string text)
+# Replace the target text with the argument text but ignore prefix and suffix that
+# are the same as current.
+fun position ReplaceTargetMinimal=2779(position length, string text)
+
# Search for a counted string in the target and set the target to the found
# range. Text is counted so it can contain NULs.
# Returns start of found range or -1 for failure in which case target is not moved.
diff --git a/include/ScintillaCall.h b/include/ScintillaCall.h
index 9f3b7e40a..9c851d567 100644
--- a/include/ScintillaCall.h
+++ b/include/ScintillaCall.h
@@ -71,6 +71,7 @@ public:
std::string StringOfRange(Span span);
Position ReplaceTarget(std::string_view text);
Position ReplaceTargetRE(std::string_view text);
+ Position ReplaceTargetMinimal(std::string_view text);
Position SearchInTarget(std::string_view text);
Span SpanSearchInTarget(std::string_view text);
@@ -393,6 +394,7 @@ public:
void TargetWholeDocument();
Position ReplaceTarget(Position length, const char *text);
Position ReplaceTargetRE(Position length, const char *text);
+ Position ReplaceTargetMinimal(Position length, const char *text);
Position SearchInTarget(Position length, const char *text);
void SetSearchFlags(Scintilla::FindOption searchFlags);
Scintilla::FindOption SearchFlags();
diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h
index 9d3034f79..12b2c2504 100644
--- a/include/ScintillaMessages.h
+++ b/include/ScintillaMessages.h
@@ -323,6 +323,7 @@ enum class Message {
TargetWholeDocument = 2690,
ReplaceTarget = 2194,
ReplaceTargetRE = 2195,
+ ReplaceTargetMinimal = 2779,
SearchInTarget = 2197,
SetSearchFlags = 2198,
GetSearchFlags = 2199,
diff --git a/src/Document.cxx b/src/Document.cxx
index 0237cef78..6a638471d 100644
--- a/src/Document.cxx
+++ b/src/Document.cxx
@@ -1242,6 +1242,17 @@ void Document::CheckReadOnly() {
}
}
+void Document::TrimReplacement(std::string_view &text, Range &range) const noexcept {
+ while (!text.empty() && !range.Empty() && (text.front() == CharAt(range.start))) {
+ text.remove_prefix(1);
+ range.start++;
+ }
+ while (!text.empty() && !range.Empty() && (text.back() == CharAt(range.end-1))) {
+ text.remove_suffix(1);
+ range.end--;
+ }
+}
+
// Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.
// SetStyleAt does not change the persistent state of a document
diff --git a/src/Document.h b/src/Document.h
index ae784180a..ac9c6670c 100644
--- a/src/Document.h
+++ b/src/Document.h
@@ -46,6 +46,10 @@ public:
return (start != Sci::invalidPosition) && (end != Sci::invalidPosition);
}
+ [[nodiscard]] bool Empty() const noexcept {
+ return start == end;
+ }
+
Sci::Position First() const noexcept {
return (start <= end) ? start : end;
}
@@ -369,6 +373,7 @@ public:
// Gateways to modifying document
void ModifiedAt(Sci::Position pos) noexcept;
void CheckReadOnly();
+ void TrimReplacement(std::string_view &text, Range &range) const noexcept;
bool DeleteChars(Sci::Position pos, Sci::Position len);
Sci::Position InsertString(Sci::Position position, const char *s, Sci::Position insertLength);
Sci::Position InsertString(Sci::Position position, std::string_view sv);
diff --git a/src/Editor.cxx b/src/Editor.cxx
index 2e1467e32..1fb6d960d 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -5696,15 +5696,27 @@ Sci::Position Editor::GetTag(char *tagValue, int tagNumber) {
return length;
}
-Sci::Position Editor::ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length) {
+Sci::Position Editor::ReplaceTarget(ReplaceType replaceType, std::string_view text) {
UndoGroup ug(pdoc);
- if (length == -1)
- length = strlen(text);
- if (replacePatterns) {
- text = pdoc->SubstituteByPosition(text, &length);
- if (!text) {
+ if (replaceType == ReplaceType::patterns) {
+ Sci::Position length = text.length();
+ const char *p = pdoc->SubstituteByPosition(text.data(), &length);
+ if (!p) {
return 0;
}
+ text = std::string_view(p, length);
+ }
+
+ if (replaceType == ReplaceType::minimal) {
+ // Check for prefix and suffix and reduce text and target to match.
+ // This is performed with Range which doesn't support virtual space.
+ Range range(targetRange.start.Position(), targetRange.end.Position());
+ pdoc->TrimReplacement(text, range);
+ // Re-apply virtual space to start if start position didn't change.
+ // Don't bother with end as its virtual space is not used
+ const SelectionPosition start(range.start == targetRange.start.Position() ?
+ targetRange.start : SelectionPosition(range.start));
+ targetRange = SelectionSegment(start, SelectionPosition(range.end));
}
// Remove the text inside the range
@@ -5718,9 +5730,9 @@ Sci::Position Editor::ReplaceTarget(bool replacePatterns, const char *text, Sci:
targetRange.end = targetRange.start;
// Insert the new text
- const Sci::Position lengthInserted = pdoc->InsertString(targetRange.start.Position(), text, length);
+ const Sci::Position lengthInserted = pdoc->InsertString(targetRange.start.Position(), text);
targetRange.end.SetPosition(targetRange.start.Position() + lengthInserted);
- return length;
+ return text.length();
}
bool Editor::IsUnicodeMode() const noexcept {
@@ -6240,11 +6252,15 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case Message::ReplaceTarget:
PLATFORM_ASSERT(lParam);
- return ReplaceTarget(false, ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
+ return ReplaceTarget(ReplaceType::basic, ViewFromParams(lParam, wParam));
case Message::ReplaceTargetRE:
PLATFORM_ASSERT(lParam);
- return ReplaceTarget(true, ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
+ return ReplaceTarget(ReplaceType::patterns, ViewFromParams(lParam, wParam));
+
+ case Message::ReplaceTargetMinimal:
+ PLATFORM_ASSERT(lParam);
+ return ReplaceTarget(ReplaceType::minimal, ViewFromParams(lParam, wParam));
case Message::SearchInTarget:
PLATFORM_ASSERT(lParam);
diff --git a/src/Editor.h b/src/Editor.h
index 128abf234..85d95e21d 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -581,7 +581,8 @@ protected: // ScintillaBase subclass needs access to much of Editor
void FoldAll(Scintilla::FoldAction action);
Sci::Position GetTag(char *tagValue, int tagNumber);
- Sci::Position ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length=-1);
+ enum class ReplaceType {basic, patterns, minimal};
+ Sci::Position ReplaceTarget(ReplaceType replaceType, std::string_view text);
bool PositionIsHotspot(Sci::Position position) const noexcept;
bool PointIsHotspot(Point pt);
@@ -625,6 +626,12 @@ protected: // ScintillaBase subclass needs access to much of Editor
static unsigned char *UCharPtrFromSPtr(Scintilla::sptr_t lParam) noexcept {
return static_cast<unsigned char *>(PtrFromSPtr(lParam));
}
+ static std::string_view ViewFromParams(Scintilla::sptr_t lParam, Scintilla::uptr_t wParam) noexcept {
+ if (SPtrFromUPtr(wParam) == -1) {
+ return std::string_view(CharPtrFromSPtr(lParam));
+ }
+ return std::string_view(CharPtrFromSPtr(lParam), wParam);
+ }
static void *PtrFromUPtr(Scintilla::uptr_t wParam) noexcept {
return reinterpret_cast<void *>(wParam);
}
diff --git a/test/simpleTests.py b/test/simpleTests.py
index 2b5d3bc0e..31ae42535 100644
--- a/test/simpleTests.py
+++ b/test/simpleTests.py
@@ -634,6 +634,45 @@ class TestSimple(unittest.TestCase):
self.assertEquals(self.ed.TargetStart, 4)
self.assertEquals(self.ed.TargetEnd, 5)
+ def testReplaceTargetMinimal(self):
+ # 1: No common characters
+ self.ed.SetContents(b"abcd")
+ self.ed.TargetStart = 1
+ self.ed.TargetEnd = 3
+ self.assertEquals(self.ed.TargetStart, 1)
+ self.assertEquals(self.ed.TargetEnd, 3)
+ rep = b"321"
+ self.ed.ReplaceTargetMinimal(len(rep), rep)
+ self.assertEquals(self.ed.Contents(), b"a321d")
+
+ # 2: New characters with common prefix and suffix
+ self.ed.TargetStart = 1
+ self.ed.TargetEnd = 4
+ rep = b"3<>1"
+ self.ed.ReplaceTargetMinimal(len(rep), rep)
+ self.assertEquals(self.ed.Contents(), b"a3<>1d")
+
+ # 3: Remove characters with common prefix and suffix
+ self.ed.TargetStart = 1
+ self.ed.TargetEnd = 5
+ rep = b"31"
+ self.ed.ReplaceTargetMinimal(len(rep), rep)
+ self.assertEquals(self.ed.Contents(), b"a31d")
+
+ # 4: Common prefix
+ self.ed.TargetStart = 1
+ self.ed.TargetEnd = 3
+ rep = b"3bc"
+ self.ed.ReplaceTargetMinimal(len(rep), rep)
+ self.assertEquals(self.ed.Contents(), b"a3bcd")
+
+ # 5: Common suffix
+ self.ed.TargetStart = 2
+ self.ed.TargetEnd = 5
+ rep = b"cd"
+ self.ed.ReplaceTargetMinimal(len(rep), rep)
+ self.assertEquals(self.ed.Contents(), b"a3cd")
+
def testTargetWhole(self):
self.ed.SetContents(b"abcd")
self.ed.TargetStart = 1