aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.hgtags2
-rw-r--r--cocoa/Scintilla/Info.plist2
-rw-r--r--cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj8
-rw-r--r--cocoa/ScintillaCocoa.mm2
-rw-r--r--cppcheck.suppress56
-rw-r--r--doc/ScintillaDoc.html20
-rw-r--r--doc/ScintillaDownload.html10
-rw-r--r--doc/ScintillaHistory.html44
-rw-r--r--doc/index.html11
-rwxr-xr-xgtk/ScintillaGTK.cxx2
-rw-r--r--include/Scintilla.h2
-rw-r--r--include/Scintilla.iface3
-rw-r--r--include/ScintillaTypes.h2
-rw-r--r--qt/ScintillaEdit/ScintillaEdit.pro2
-rw-r--r--qt/ScintillaEditBase/PlatQt.cpp5
-rw-r--r--qt/ScintillaEditBase/PlatQt.h9
-rw-r--r--qt/ScintillaEditBase/ScintillaEditBase.cpp16
-rw-r--r--qt/ScintillaEditBase/ScintillaEditBase.pro2
-rw-r--r--qt/ScintillaEditBase/ScintillaQt.cpp2
-rw-r--r--scripts/CheckMentioned.py4
-rw-r--r--scripts/Dependencies.py30
-rw-r--r--scripts/Face.py4
-rw-r--r--scripts/FileGenerator.py6
-rw-r--r--scripts/GenerateCharacterCategory.py4
-rw-r--r--scripts/ScintillaData.py21
-rw-r--r--src/CellBuffer.cxx103
-rw-r--r--src/CellBuffer.h29
-rw-r--r--src/CharacterType.h38
-rw-r--r--src/Document.cxx52
-rw-r--r--src/Document.h1
-rw-r--r--src/EditView.cxx63
-rw-r--r--src/Editor.cxx21
-rw-r--r--src/Editor.h4
-rw-r--r--test/simpleTests.py6
-rw-r--r--test/unit/testDocument.cxx97
-rw-r--r--version.txt2
-rw-r--r--win32/ListBox.cxx25
-rw-r--r--win32/ScintRes.rc4
-rw-r--r--win32/ScintillaWin.cxx17
39 files changed, 556 insertions, 175 deletions
diff --git a/.hgtags b/.hgtags
index cec25cb57..8571ee838 100644
--- a/.hgtags
+++ b/.hgtags
@@ -219,3 +219,5 @@ ba0e2f317940a65f8f16325cbe4234fd75a6d47b rel-5-5-7
afc75d68d333f449ad52fce35ff40a5eddadf5c8 rel-5-5-8
76c8666d51407d156417e4b4a293a5e9ed29087f rel-5-5-9
a951931386960b1c5045652c9b93e79b669e9728 rel-5-6-0
+5ab4c75c3d5d252de41d3481fbcb8212e05f96d0 rel-5-6-1
+4d4244525b8a4327e8a04d5cc2cbb79bea0fc574 rel-5-6-2
diff --git a/cocoa/Scintilla/Info.plist b/cocoa/Scintilla/Info.plist
index 195a18222..4e20666ec 100644
--- a/cocoa/Scintilla/Info.plist
+++ b/cocoa/Scintilla/Info.plist
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
- <string>5.6.0</string>
+ <string>5.6.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
diff --git a/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj b/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj
index eb28817b1..58df01a1e 100644
--- a/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj
+++ b/cocoa/Scintilla/Scintilla.xcodeproj/project.pbxproj
@@ -586,7 +586,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 5.6.0;
+ CURRENT_PROJECT_VERSION = 5.6.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -650,7 +650,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 5.6.0;
+ CURRENT_PROJECT_VERSION = 5.6.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
@@ -682,7 +682,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 5.6.0;
+ CURRENT_PROJECT_VERSION = 5.6.2;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
@@ -717,7 +717,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 5.6.0;
+ CURRENT_PROJECT_VERSION = 5.6.2;
DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm
index 8a9af4058..cc4e01c34 100644
--- a/cocoa/ScintillaCocoa.mm
+++ b/cocoa/ScintillaCocoa.mm
@@ -963,6 +963,8 @@ sptr_t ScintillaCocoa::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
}
} catch (std::bad_alloc &) {
errorStatus = Status::BadAlloc;
+ } catch (Failure &failure) {
+ errorStatus = failure.status;
} catch (...) {
errorStatus = Status::Failure;
}
diff --git a/cppcheck.suppress b/cppcheck.suppress
index 812b65277..dd0af696b 100644
--- a/cppcheck.suppress
+++ b/cppcheck.suppress
@@ -1,6 +1,6 @@
// File to suppress cppcheck warnings for files that will not be fixed.
// Does not suppress warnings where an additional occurrence of the warning may be of interest.
-// Configured for cppcheck 2.15
+// Configured for cppcheck 2.20
// Just a report of how many checkers are run
checkersReport
@@ -50,11 +50,31 @@ knownConditionTrueFalse:scintilla/src/EditView.cxx
// This makes no sense
CastIntegerToAddressAtReturn:scintilla\src\DBCS.cxx
+// Preprocessor #if with *_CHECK_VERSION(...) *_VERSION_CHECK(...)
+syntaxError:scintilla/gtk/Wrappers.h
+syntaxError:scintilla/gtk/PlatGTK.cxx
+syntaxError:scintilla/gtk/ScintillaGTKAccessible.cxx
+syntaxError:scintilla/qt/ScintillaEditBase/PlatQt.cpp
+syntaxError:scintilla/qt/ScintillaEditBase/ScintillaEditBase.cpp
+syntaxError:scintilla/qt/ScintillaEditBase/ScintillaQt.cpp
+
+noValidConfiguration:scintilla/gtk/PlatGTK.cxx
+noValidConfiguration:scintilla/gtk/ScintillaGTKAccessible.cxx
+
+unmatchedSuppression:scintilla/gtk/PlatGTK.cxx
+unmatchedSuppression:scintilla/gtk/ScintillaGTKAccessible.cxx
+unmatchedSuppression:scintilla/qt/ScintillaEditBase/PlatQt.cpp
+unmatchedSuppression:scintilla/qt/ScintillaEditBase/ScintillaEditBase.cpp
+unmatchedSuppression:scintilla/qt/ScintillaEditBase/ScintillaQt.cpp
+
// G_DEFINE_TYPE is too complex to pass to cppcheck
unknownMacro:scintilla/gtk/PlatGTK.cxx
// G_END_DECLS
unknownMacro:scintilla/gtk/scintilla-marshal.h
+// Marshaling hidden from cppcheck
+unusedFunction:scintilla/gtk/scintilla-marshal.c
+
// Difficult to test accessibility so don't change
constParameterPointer:scintilla/gtk/ScintillaGTKAccessible.cxx
constVariableReference:scintilla/gtk/ScintillaGTKAccessible.cxx
@@ -66,16 +86,48 @@ preprocessorErrorDirective:scintilla/qt/**.cpp
// Doesn't understand Qt slots macro
unknownMacro:scintilla/qt/ScintillaEditBase/*.h
+// Provided for other projects
+unusedFunction:scintilla/qt/ScintillaEdit/ScintillaDocument.cpp
+
+// Provided for other projects
+unusedFunction:scintilla/call/ScintillaCall.cxx
+
+// Don't want to unnecessarily allow const pointers to be made mutable through reinterpret_cast
+constParameterPointer:scintilla/call/ScintillaCall.cxx
+constParameterPointer:scintilla/src/Editor.cxx
+constVariablePointer:scintilla/src/Editor.cxx
+
// The performance cost of by-value passing is often small and using a reference decreases
// code legibility.
passedByValue
+// For conciseness, the testing code is much sloppier than the library
+
// Suppress everything in catch.hpp as won't be changing
*:scintilla/test/unit/catch.hpp
-// Checks for moves move to variables that are not read but the moved from is checked
+// unread variables and overwritten without read in tests
unreadVariable:scintilla/test/unit/*.cxx
+
+// Checks for moves move to variables that are not read but the moved from is checked
accessMoved:scintilla/test/unit/*.cxx
+// 'obviously' true tests are still useful
+knownConditionTrueFalse:scintilla/test/unit/*.cxx
+
+// fields in TEST_CASE could be restricted to SECTION but not interesting
+variableScope:scintilla/test/unit/*.cxx
+
+// overwritten without read in tests
+redundantInitialization:scintilla/test/unit/*.cxx
+redundantAssignment:scintilla/test/unit/*.cxx
+unusedVariable:scintilla/test/unit/*.cxx
+
+// doesn't understand cppcheck TEST_CASE
+syntaxError:scintilla/test/unit/*.cxx
+
+// doesn't understand cppcheck generated functions
+unusedFunction:scintilla/test/unit/*.cxx
+
// cppcheck fails REQUIRE from Catch
comparisonOfFuncReturningBoolError:scintilla/test/unit/*.cxx
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html
index d432043f6..c21bd2e29 100644
--- a/doc/ScintillaDoc.html
+++ b/doc/ScintillaDoc.html
@@ -959,6 +959,12 @@ struct Sci_TextRangeFull {
</tr>
<tr>
+ <th align="left"><code>SC_STATUS_OUTSIDE_DOCUMENT</code></th>
+ <td>3</td>
+ <td>An operation was attempted on a position that is outside the document</td>
+ </tr>
+
+ <tr>
<th align="left"><code>SC_STATUS_WARN_REGEX</code></th>
<td>1001</td>
<td>Regular expression is invalid</td>
@@ -967,6 +973,10 @@ struct Sci_TextRangeFull {
</tbody>
</table>
+ <p>To more easily check the status of APIs, applications should call the direct status function using
+ <a class="message" href="#SCI_GETDIRECTSTATUSFUNCTION"><code>SCI_GETDIRECTSTATUSFUNCTION</code></a>.
+ </p>
+
<h2 id="Selection">Selection</h2>
<p>Scintilla maintains a selection that stretches between two points, the anchor and the
@@ -2890,6 +2900,16 @@ struct Sci_TextToFindFull {
<td>A horizontal line stretching until the tabstop.</td>
</tr>
+
+ <tr>
+ <th align="left"><code>SCTD_CONTROLCHAR</code></th>
+
+ <td>2</td>
+
+ <td>Will be drawn as a control code according to the configured
+ <a href="#CharacterRepresentations">character representation</a>
+ without any indentation.</td>
+ </tr>
</tbody>
</table>
diff --git a/doc/ScintillaDownload.html b/doc/ScintillaDownload.html
index 59ef4b721..0af09cf9d 100644
--- a/doc/ScintillaDownload.html
+++ b/doc/ScintillaDownload.html
@@ -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/scintilla560.zip">
+ <font size="4"> <a href="https://www.scintilla.org/scintilla562.zip">
Windows</a>&nbsp;&nbsp;
- <a href="https://www.scintilla.org/scintilla560.tgz">
+ <a href="https://www.scintilla.org/scintilla562.tgz">
GTK/Linux</a>&nbsp;&nbsp;
</font>
</td>
@@ -42,7 +42,7 @@
containing very few restrictions.
</p>
<h3>
- Release 5.6.0
+ Release 5.6.2
</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/scintilla560.zip">zip format</a> (1.8M) commonly used on Windows</li>
- <li><a href="https://www.scintilla.org/scintilla560.tgz">tgz format</a> (1.7M) commonly used on Linux and compatible operating systems</li>
+ <li><a href="https://www.scintilla.org/scintilla562.zip">zip format</a> (1.8M) commonly used on Windows</li>
+ <li><a href="https://www.scintilla.org/scintilla562.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>
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html
index b91a7e282..bcbfa53a4 100644
--- a/doc/ScintillaHistory.html
+++ b/doc/ScintillaHistory.html
@@ -593,10 +593,54 @@
<td>Sven Ritter</td>
<td>Stefan Löffler</td>
<td>Nathaniel Braun</td>
+ </tr><tr>
+ <td>Stephan T. Lavavej</td>
</tr>
</table>
<h2 id="Releases">Releases</h2>
<h3>
+ <a href="https://www.scintilla.org/scintilla562.zip">Release 5.6.2</a>
+ </h3>
+ <ul>
+ <li>
+ Released 29 April 2026.
+ </li>
+ <li>
+ Add error status SC_STATUS_OUTSIDE_DOCUMENT that is set when operations are attempted on a position
+ outside the document.
+ Positions are checked earlier to prevent actions partly succeeding.
+ Incomplete actions could lead to features that are out of synchronization
+ like change history referring to text that has been deleted.
+ </li>
+ <li>
+ On Win32 with DirectWrite in a GDI scaled application, draw sharper text in autocompletion lists.
+ <a href="https://sourceforge.net/p/scintilla/bugs/2505/">Bug #2505</a>.
+ </li>
+ <li>
+ On Win32 update scaling when the application moves to another monitor.
+ <a href="https://sourceforge.net/p/scintilla/bugs/2503/">Bug #2503</a>.
+ </li>
+ </ul>
+ <h3>
+ <a href="https://www.scintilla.org/scintilla561.zip">Release 5.6.1</a>
+ </h3>
+ <ul>
+ <li>
+ Released 26 March 2026.
+ </li>
+ <li>
+ Add mode to draw tabs as [HT] blobs with SCI_SETTABDRAWMODE(SCTD_CONTROLCHAR).
+ </li>
+ <li>
+ Improve mouse drag behaviour when drag and drop disabled.
+ <a href="https://sourceforge.net/p/scintilla/feature-requests/1581/">Feature #1581</a>.
+ </li>
+ <li>
+ Fix regression in SCI_CONVERTEOLS that may not convert whole file.
+ <a href="https://sourceforge.net/p/scintilla/bugs/2501/">Bug #2501</a>.
+ </li>
+ </ul>
+ <h3>
<a href="https://www.scintilla.org/scintilla560.zip">Release 5.6.0</a>
</h3>
<ul>
diff --git a/doc/index.html b/doc/index.html
index 69fe4a270..5a67a95a1 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -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="20260225" />
+ <meta name="Date.Modified" content="20260429" />
<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.6.0<br />
- Site last modified February 25 2026</font>
+ <font color="#FFCC99" size="3"> Release version 5.6.2<br />
+ Site last modified April 29 2026</font>
</td>
<td width="20%">
&nbsp;
@@ -77,12 +77,11 @@
</tr>
</table>
<ul id="versionlist">
+ <li>Version 5.6.2 adds error status SC_STATUS_OUTSIDE_DOCUMENT and stops out-of-bounds insertions earlier.</li>
+ <li>Version 5.6.1 adds mode to draw tabs as HT blobs and fixes a regression in SCI_CONVERTEOLS.</li>
<li>Version 5.6.0 fixes crash when window is too narrow to show any text.</li>
<li>Version 5.5.9 adds an option to disable drag &amp; drop. Fixes colour after line end.</li>
<li>Version 5.5.8 changes format of SCI_GETSELECTIONSERIALIZED and fixes redraw after undo.</li>
- <li>Version 5.5.7 can prevent storing scroll position in undo selection history and adds a wrap-aware SCI_SCROLLVERTICAL API.</li>
- <li>Version 5.5.6 improves DBCS and autocompletion drawing on Win32 and improves dwell, encoding and text clipping on Qt.</li>
- <li>Version 5.5.5 can remember selection with undo and redo. Update to use DirectWrite 1.1.</li>
</ul>
<ul id="menu">
<li id="remote1"><a href="https://www.scintilla.org/SciTEImage.html">Screenshot</a></li>
diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx
index 686a8c13f..b8a7c354a 100755
--- a/gtk/ScintillaGTK.cxx
+++ b/gtk/ScintillaGTK.cxx
@@ -947,6 +947,8 @@ sptr_t ScintillaGTK::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
}
} catch (std::bad_alloc &) {
errorStatus = Status::BadAlloc;
+ } catch (Failure &failure) {
+ errorStatus = failure.status;
} catch (...) {
errorStatus = Status::Failure;
}
diff --git a/include/Scintilla.h b/include/Scintilla.h
index b6786097e..7f12703d3 100644
--- a/include/Scintilla.h
+++ b/include/Scintilla.h
@@ -78,6 +78,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SCI_SETVIEWWS 2021
#define SCTD_LONGARROW 0
#define SCTD_STRIKEOUT 1
+#define SCTD_CONTROLCHAR 2
#define SCI_GETTABDRAWMODE 2698
#define SCI_SETTABDRAWMODE 2699
#define SCI_POSITIONFROMPOINT 2022
@@ -862,6 +863,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SC_STATUS_OK 0
#define SC_STATUS_FAILURE 1
#define SC_STATUS_BADALLOC 2
+#define SC_STATUS_OUTSIDE_DOCUMENT 3
#define SC_STATUS_WARN_START 1000
#define SC_STATUS_WARN_REGEX 1001
#define SCI_SETSTATUS 2382
diff --git a/include/Scintilla.iface b/include/Scintilla.iface
index 6262f7c1b..ddb025c50 100644
--- a/include/Scintilla.iface
+++ b/include/Scintilla.iface
@@ -196,9 +196,11 @@ set void SetViewWS=2021(WhiteSpace viewWS,)
enu TabDrawMode=SCTD_
val SCTD_LONGARROW=0
val SCTD_STRIKEOUT=1
+val SCTD_CONTROLCHAR=2
ali SCTD_LONGARROW=LONG_ARROW
ali SCTD_STRIKEOUT=STRIKE_OUT
+ali SCTD_CONTROLCHAR=CONTROL_CHAR
# Retrieve the current tab draw mode.
# Returns one of SCTD_* constants.
@@ -2289,6 +2291,7 @@ enu Status=SC_STATUS_
val SC_STATUS_OK=0
val SC_STATUS_FAILURE=1
val SC_STATUS_BADALLOC=2
+val SC_STATUS_OUTSIDE_DOCUMENT=3
val SC_STATUS_WARN_START=1000
val SC_STATUS_WARN_REGEX=1001
diff --git a/include/ScintillaTypes.h b/include/ScintillaTypes.h
index 0991a1480..cc5f03815 100644
--- a/include/ScintillaTypes.h
+++ b/include/ScintillaTypes.h
@@ -26,6 +26,7 @@ enum class WhiteSpace {
enum class TabDrawMode {
LongArrow = 0,
StrikeOut = 1,
+ ControlChar = 2,
};
enum class EndOfLine {
@@ -431,6 +432,7 @@ enum class Status {
Ok = 0,
Failure = 1,
BadAlloc = 2,
+ OutsideDocument = 3,
WarnStart = 1000,
RegEx = 1001,
};
diff --git a/qt/ScintillaEdit/ScintillaEdit.pro b/qt/ScintillaEdit/ScintillaEdit.pro
index 295678d3c..c636e0659 100644
--- a/qt/ScintillaEdit/ScintillaEdit.pro
+++ b/qt/ScintillaEdit/ScintillaEdit.pro
@@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += c++1z
-VERSION = 5.6.0
+VERSION = 5.6.2
SOURCES += \
ScintillaEdit.cpp \
diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp
index 38aec01a0..96e7f2865 100644
--- a/qt/ScintillaEditBase/PlatQt.cpp
+++ b/qt/ScintillaEditBase/PlatQt.cpp
@@ -1378,8 +1378,13 @@ void Platform::Assert(const char *c, const char *file, int line) noexcept
char buffer[2000];
snprintf(buffer, std::size(buffer), "Assertion [%s] failed at %s %d", c, file, line);
if (Platform::ShowAssertionPopUps(false)) {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
+ QMessageBox mb(QMessageBox::Icon::NoIcon, "Assertion Failure", buffer,
+ QMessageBox::StandardButton::Ok);
+#else
QMessageBox mb("Assertion Failure", buffer, QMessageBox::NoIcon,
QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
+#endif
mb.exec();
} else {
strcat(buffer, "\n");
diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h
index 08cda9dc4..a4d924014 100644
--- a/qt/ScintillaEditBase/PlatQt.h
+++ b/qt/ScintillaEditBase/PlatQt.h
@@ -61,13 +61,14 @@ inline Point PointFromQPoint(QPoint qp)
return Point(qp.x(), qp.y());
}
-inline QPointF QPointFFromPoint(Point qp)
+inline Point PointFromQPointF(QPointF qp)
{
- return QPointF(qp.x, qp.y);
+ return Point(qp.x(), qp.y());
}
-constexpr PRectangle RectangleInset(PRectangle rc, XYPOSITION delta) noexcept {
- return PRectangle(rc.left + delta, rc.top + delta, rc.right - delta, rc.bottom - delta);
+inline QPointF QPointFFromPoint(Point qp)
+{
+ return QPointF(qp.x, qp.y);
}
class SurfaceImpl : public Surface {
diff --git a/qt/ScintillaEditBase/ScintillaEditBase.cpp b/qt/ScintillaEditBase/ScintillaEditBase.cpp
index 187da9dbe..477dd44cf 100644
--- a/qt/ScintillaEditBase/ScintillaEditBase.cpp
+++ b/qt/ScintillaEditBase/ScintillaEditBase.cpp
@@ -304,6 +304,16 @@ unsigned int TimeOfEvent(const QElapsedTimer &timer)
return static_cast<unsigned int>(timer.elapsed() % maxTime);
}
+Point PointOfEvent(const QDropEvent *event)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ PLATFORM_ASSERT(false);
+ return PointFromQPointF(event->position());
+#else
+ return PointFromQPoint(event->pos());
+#endif
+}
+
}
void ScintillaEditBase::mousePressEvent(QMouseEvent *event)
@@ -396,7 +406,7 @@ void ScintillaEditBase::dragEnterEvent(QDragEnterEvent *event)
} else if (event->mimeData()->hasText()) {
event->acceptProposedAction();
- const Point point = PointFromQPoint(event->pos());
+ const Point point = PointOfEvent(event);
sqt->DragEnter(point);
} else {
event->ignore();
@@ -415,7 +425,7 @@ void ScintillaEditBase::dragMoveEvent(QDragMoveEvent *event)
} else if (event->mimeData()->hasText()) {
event->acceptProposedAction();
- const Point point = PointFromQPoint(event->pos());
+ const Point point = PointOfEvent(event);
sqt->DragMove(point);
} else {
event->ignore();
@@ -430,7 +440,7 @@ void ScintillaEditBase::dropEvent(QDropEvent *event)
} else if (event->mimeData()->hasText()) {
event->acceptProposedAction();
- const Point point = PointFromQPoint(event->pos());
+ const Point point = PointOfEvent(event);
const bool move = (event->source() == this &&
event->proposedAction() == Qt::MoveAction);
sqt->Drop(point, event->mimeData(), move);
diff --git a/qt/ScintillaEditBase/ScintillaEditBase.pro b/qt/ScintillaEditBase/ScintillaEditBase.pro
index d2ab750df..6e95cca86 100644
--- a/qt/ScintillaEditBase/ScintillaEditBase.pro
+++ b/qt/ScintillaEditBase/ScintillaEditBase.pro
@@ -13,7 +13,7 @@ TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += c++1z
-VERSION = 5.6.0
+VERSION = 5.6.2
SOURCES += \
PlatQt.cpp \
diff --git a/qt/ScintillaEditBase/ScintillaQt.cpp b/qt/ScintillaEditBase/ScintillaQt.cpp
index d5e6eb7b0..5e9d262a4 100644
--- a/qt/ScintillaEditBase/ScintillaQt.cpp
+++ b/qt/ScintillaEditBase/ScintillaQt.cpp
@@ -768,6 +768,8 @@ sptr_t ScintillaQt::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam)
}
} catch (std::bad_alloc &) {
errorStatus = Status::BadAlloc;
+ } catch (Failure &failure) {
+ errorStatus = failure.status;
} catch (...) {
errorStatus = Status::Failure;
}
diff --git a/scripts/CheckMentioned.py b/scripts/CheckMentioned.py
index 1e5f6c393..b6d52293e 100644
--- a/scripts/CheckMentioned.py
+++ b/scripts/CheckMentioned.py
@@ -35,13 +35,13 @@ def depunctuate(s):
symbols = {}
with open(incFileName, "rt") as incFile:
- for line in incFile.readlines():
+ for line in incFile:
if line.startswith("#define"):
identifier = line.split()[1]
symbols[identifier] = 0
with open(docFileName, "rt") as docFile:
- for line in docFile.readlines():
+ for line in docFile:
for word in depunctuate(line).split():
if word in symbols.keys():
symbols[word] = 1
diff --git a/scripts/Dependencies.py b/scripts/Dependencies.py
index 135c57ac8..51e332c56 100644
--- a/scripts/Dependencies.py
+++ b/scripts/Dependencies.py
@@ -17,9 +17,10 @@
# Only tested with ASCII file names.
# Copyright 2019 by Neil Hodgson <neilh@scintilla.org>
# The License.txt file describes the conditions under which this software may be distributed.
-# Requires Python 2.7 or later
+# Requires Python 3.6 or later
import codecs, glob, os, sys
+from functools import cache
if __name__ == "__main__":
import FileGenerator
@@ -28,6 +29,7 @@ else:
continuationLineEnd = " \\"
+@cache
def FindPathToHeader(header, includePath):
for incDir in includePath:
relPath = os.path.join(incDir, header)
@@ -35,27 +37,25 @@ def FindPathToHeader(header, includePath):
return relPath
return ""
-fhifCache = {} # Remember the includes in each file. ~5x speed up.
+@cache
def FindHeadersInFile(filePath):
- if filePath not in fhifCache:
- headers = []
- with codecs.open(filePath, "r", "utf-8") as f:
- for line in f:
- if line.strip().startswith("#include"):
- parts = line.split()
- if len(parts) > 1:
- header = parts[1]
- if header[0] != '<': # No system headers
- headers.append(header.strip('"'))
- fhifCache[filePath] = headers
- return fhifCache[filePath]
+ headers = []
+ with codecs.open(filePath, "r", "utf-8") as f:
+ for line in f:
+ if line.strip().startswith("#include"):
+ parts = line.split()
+ if len(parts) > 1:
+ header = parts[1]
+ if header[0] != '<': # No system headers
+ headers.append(header.strip('"'))
+ return headers
def FindHeadersInFileRecursive(filePath, includePath, renames):
headerPaths = []
for header in FindHeadersInFile(filePath):
if header in renames:
header = renames[header]
- relPath = FindPathToHeader(header, includePath)
+ relPath = FindPathToHeader(header, tuple(includePath))
if relPath and relPath not in headerPaths:
headerPaths.append(relPath)
subHeaders = FindHeadersInFileRecursive(relPath, includePath, renames)
diff --git a/scripts/Face.py b/scripts/Face.py
index 9814115f0..0278f5ac2 100644
--- a/scripts/Face.py
+++ b/scripts/Face.py
@@ -2,7 +2,7 @@
# Face.py - module for reading and parsing Scintilla.iface file
# Implemented 2000 by Neil Hodgson neilh@scintilla.org
# Released to the public domain.
-# Requires Python 2.7 or later
+# Requires Python 3.6 or later
def sanitiseLine(line):
line = line.rstrip('\n')
@@ -72,7 +72,7 @@ class Face:
currentComment = []
currentCommentFinished = 0
file = open(name)
- for line in file.readlines():
+ for line in file:
line = sanitiseLine(line)
if line:
if line[0] == "#":
diff --git a/scripts/FileGenerator.py b/scripts/FileGenerator.py
index f798e1d6b..f596e9cbe 100644
--- a/scripts/FileGenerator.py
+++ b/scripts/FileGenerator.py
@@ -4,7 +4,7 @@
# Generate or regenerate source files based on comments in those files.
# May be modified in-place or a template may be generated into a complete file.
-# Requires Python 2.7 or later
+# Requires Python 3.6 or later
# The files are copied to a string apart from sections between a
# ++Autogenerated comment and a --Autogenerated comment which is
# generated by the CopyWithInsertion function. After the whole string is
@@ -143,7 +143,7 @@ def UpdateLineInPlistFile(path, key, value):
lines = []
keyCurrent = ""
with codecs.open(path, "rb", "utf-8") as f:
- for line in f.readlines():
+ for line in f:
ls = line.strip()
if ls.startswith("<key>"):
keyCurrent = ls.replace("<key>", "").replace("</key>", "")
@@ -160,7 +160,7 @@ def UpdateLineInFile(path, linePrefix, lineReplace):
lines = []
updated = False
with codecs.open(path, "r", "utf-8") as f:
- for line in f.readlines():
+ for line in f:
line = line.rstrip()
if not updated and line.startswith(linePrefix):
lines.append(lineReplace)
diff --git a/scripts/GenerateCharacterCategory.py b/scripts/GenerateCharacterCategory.py
index 806dea2fe..1080cceed 100644
--- a/scripts/GenerateCharacterCategory.py
+++ b/scripts/GenerateCharacterCategory.py
@@ -2,7 +2,7 @@
# Script to generate scintilla/src/CharacterCategoryMap.cxx and lexilla/lexlib/CharacterCategory.cxx
# from Python's Unicode data
# Should be run rarely when a Python with a new version of Unicode data is available.
-# Requires Python 3.3 or later
+# Requires Python 3.6 or later
# Should not be run with old versions of Python.
import pathlib, platform, sys, unicodedata
@@ -11,7 +11,7 @@ from FileGenerator import Regenerate
def findCategories(filename):
with filename.open(encoding="UTF-8") as infile:
- lines = [x.strip() for x in infile.readlines() if "\tcc" in x]
+ lines = [x.strip() for x in infile if "\tcc" in x]
values = "".join(lines).replace(" ","").split(",")
print("Categrories:", values)
return [v[2:] for v in values]
diff --git a/scripts/ScintillaData.py b/scripts/ScintillaData.py
index 55534306b..d57898e5e 100644
--- a/scripts/ScintillaData.py
+++ b/scripts/ScintillaData.py
@@ -25,19 +25,20 @@
import datetime, pathlib, sys
def FindCredits(historyFile, removeLinks=True):
+ """ Return a list of contributors in a history file. """
credits = []
stage = 0
with historyFile.open(encoding="utf-8") as f:
- for line in f.readlines():
- line = line.strip()
- if stage == 0 and line == "<table>":
+ for line in f:
+ s = line.strip()
+ if stage == 0 and s == "<table>":
stage = 1
- elif stage == 1 and line == "</table>":
+ elif stage == 1 and s == "</table>":
stage = 2
- if stage == 1 and line.startswith("<td>"):
- credit = line[4:-5]
- if removeLinks and "<a" in line:
- title, _a, rest = credit.partition("<a href=")
+ if stage == 1 and s.startswith("<td>"):
+ credit = s[4:-5]
+ if removeLinks and "<a" in s:
+ title, _, rest = credit.partition("<a href=")
urlplus, _bracket, end = rest.partition(">")
name = end.split("<")[0]
url = urlplus[1:-1]
@@ -57,14 +58,14 @@ class ScintillaData:
self.versionCommad = self.versionDotted.replace(".", ", ") + ', 0'
with (scintillaRoot / "doc" / "index.html").open() as f:
- self.dateModified = [d for d in f.readlines() if "Date.Modified" in d]\
+ self.dateModified = [d for d in f if "Date.Modified" in d]\
[0].split('\"')[3]
# 20130602
# index.html, SciTE.html
dtModified = datetime.datetime.strptime(self.dateModified, "%Y%m%d")
self.yearModified = self.dateModified[0:4]
monthModified = dtModified.strftime("%B")
- dayModified = "%d" % dtModified.day
+ dayModified = f"{dtModified.day}"
self.mdyModified = monthModified + " " + dayModified + " " + self.yearModified
# May 22 2013
# index.html, SciTE.html
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx
index 3e9deb934..1eac3f9c2 100644
--- a/src/CellBuffer.cxx
+++ b/src/CellBuffer.cxx
@@ -439,35 +439,90 @@ const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci:
return data;
}
-bool CellBuffer::SetStyleAt(Sci::Position position, char styleValue) noexcept {
- if (!hasStyles) {
- return false;
- }
- const char curVal = style.ValueAt(position);
- if (curVal != styleValue) {
- style.SetValueAt(position, styleValue);
- return true;
- } else {
- return false;
+namespace {
+
+ChangedRange CopyBytes(char *p, const char *styles, Sci::Position length, Sci::Position offset) noexcept {
+ for (Sci::Position start = 0; start < length; start++) {
+ if (p[start] != styles[start]) {
+ for (Sci::Position end = length - 1; end >= 0; end--) {
+ if (p[end] != styles[end]) {
+ memcpy(p+start, styles+start, end-start+1);
+ return { start + offset, end + offset };
+ }
+ }
+ }
}
+ return {};
}
-bool CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept {
- if (!hasStyles) {
- return false;
- }
- bool changed = false;
- PLATFORM_ASSERT(lengthStyle == 0 ||
- (lengthStyle > 0 && lengthStyle + position <= style.Length()));
- while (lengthStyle--) {
- const char curVal = style.ValueAt(position);
- if (curVal != styleValue) {
- style.SetValueAt(position, styleValue);
- changed = true;
+ChangedRange SetBytes(char *p, char style, Sci::Position length, Sci::Position offset) noexcept {
+ for (Sci::Position start = 0; start < length; start++) {
+ if (p[start] != style) {
+ for (Sci::Position end = length - 1; end >= 0; end--) {
+ if (p[end] != style) {
+ memset(p+start, style, end-start+1);
+ return { start + offset, end + offset };
+ }
+ }
}
- position++;
}
- return changed;
+ return {};
+}
+
+struct Lengths {
+ Sci::Position length1;
+ Sci::Position length2;
+};
+
+Lengths SplitUpdate(const SplitVector<char> &style, Sci::Position position, Sci::Position length) noexcept {
+ length = std::min(length, style.Length() - position);
+
+ // Divide length into two parts if it overlaps the gap
+ const Sci::Position part1Length = style.GapPosition();
+
+ // If all after gap then place in length1 to avoid second call
+ if (position >= part1Length) {
+ return { length, 0 };
+ }
+ // If all before gap then place in length1
+ const Sci::Position beforeGap = part1Length - position;
+ if (length <= beforeGap) {
+ return { length, 0 };
+ }
+ // Some before gap and some after gap so put portion in length1 and rest in length2
+ return { beforeGap, length - beforeGap };
+}
+
+}
+
+ChangedRange CellBuffer::SetStyles(Sci::Position position, const char *styles, Sci::Position length) noexcept {
+ if (!hasStyles || (position < 0) || (length <= 0)) {
+ return {};
+ }
+
+ const Lengths lengths = SplitUpdate(style, position, length);
+ ChangedRange cr = CopyBytes(&style[position], styles, lengths.length1, position);
+ if (lengths.length2) {
+ const ChangedRange cr2 = CopyBytes(&style[position + lengths.length1], styles + lengths.length1,
+ lengths.length2, position + lengths.length1);
+ cr.Merge(cr2);
+ }
+ return cr;
+}
+
+ChangedRange CellBuffer::SetStyleFor(Sci::Position position, Sci::Position length, char value) noexcept {
+ if (!hasStyles || (position < 0) || (length <= 0)) {
+ return {};
+ }
+
+ const Lengths lengths = SplitUpdate(style, position, length);
+ ChangedRange cr = SetBytes(&style[position], value, lengths.length1, position);
+ if (lengths.length2) {
+ const ChangedRange cr2 = SetBytes(&style[position + lengths.length1], value,
+ lengths.length2, position + lengths.length1);
+ cr.Merge(cr2);
+ }
+ return cr;
}
// The char* returned is to an allocation owned by the undo history
diff --git a/src/CellBuffer.h b/src/CellBuffer.h
index 1b035597a..11479f14a 100644
--- a/src/CellBuffer.h
+++ b/src/CellBuffer.h
@@ -10,6 +10,11 @@
namespace Scintilla::Internal {
+struct Failure : public std::runtime_error {
+ Status status;
+ Failure(Status status_) : std::runtime_error("failure with status"), status(status_) {}
+};
+
// Interface to per-line data that wants to see each line insertion and deletion
class PerLine {
public:
@@ -66,6 +71,24 @@ struct SplitView {
}
};
+struct ChangedRange {
+ Sci::Position start = Sci::invalidPosition;
+ Sci::Position end = Sci::invalidPosition;
+ ChangedRange() noexcept = default;
+ ChangedRange(Sci::Position start_, Sci::Position end_) noexcept : start(start_), end(end_) {}
+ [[nodiscard]] bool Empty() const noexcept {
+ return start < 0;
+ }
+ void Merge(const ChangedRange &cr2) noexcept {
+ if (cr2.start >= 0) {
+ if (start < 0) {
+ *this = cr2;
+ } else {
+ end = cr2.end;
+ }
+ }
+ }
+};
/**
* Holder for an expandable array of characters that supports undo and line markers.
@@ -141,9 +164,9 @@ public:
const char *InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence);
/// Setting styles for positions outside the range of the buffer is safe and has no effect.
- /// @return true if the style of a character is changed.
- bool SetStyleAt(Sci::Position position, char styleValue) noexcept;
- bool SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept;
+ /// @return range where style of characters changed.
+ ChangedRange SetStyles(Sci::Position position, const char *styles, Sci::Position length) noexcept;
+ ChangedRange SetStyleFor(Sci::Position position, Sci::Position length, char value) noexcept;
const char *DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence);
diff --git a/src/CharacterType.h b/src/CharacterType.h
index 437fb8c5c..a7732cec9 100644
--- a/src/CharacterType.h
+++ b/src/CharacterType.h
@@ -12,12 +12,29 @@ namespace Scintilla::Internal {
// Functions for classifying characters
+template <typename T, typename... Args>
+constexpr bool AnyOf(T t, Args... args) noexcept {
+#if defined(__clang__)
+ static_assert(__is_integral(T) || __is_enum(T));
+#endif
+ return ((t == args) || ...);
+}
+
+// prevent pointer without <type_traits>
+template <typename T, typename... Args>
+constexpr void AnyOf([[maybe_unused]] T *t, [[maybe_unused]] Args... args) noexcept {}
+template <typename T, typename... Args>
+constexpr void AnyOf([[maybe_unused]] const T *t, [[maybe_unused]] Args... args) noexcept {}
+
/**
* Check if a character is a space.
* This is ASCII specific but is safe with chars >= 0x80.
*/
+constexpr int charTab = 0x09;
+constexpr int charCarriageReturn = 0x0D;
+
constexpr bool IsASpace(int ch) noexcept {
- return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
+ return (ch == ' ') || ((ch >= charTab) && (ch <= charCarriageReturn));
}
constexpr bool IsSpaceOrTab(int ch) noexcept {
@@ -44,17 +61,18 @@ constexpr bool IsADigit(int ch) noexcept {
}
constexpr bool IsADigit(int ch, int base) noexcept {
- if (base <= 10) {
+ constexpr int digits = 10;
+ if (base <= digits) {
return (ch >= '0') && (ch < '0' + base);
- } else {
- return ((ch >= '0') && (ch <= '9')) ||
- ((ch >= 'A') && (ch < 'A' + base - 10)) ||
- ((ch >= 'a') && (ch < 'a' + base - 10));
}
+ return ((ch >= '0') && (ch <= '9')) ||
+ ((ch >= 'A') && (ch < 'A' + base - digits)) ||
+ ((ch >= 'a') && (ch < 'a' + base - digits));
}
constexpr bool IsASCII(int ch) noexcept {
- return (ch >= 0) && (ch < 0x80);
+ constexpr int lastASCII = 0x7F;
+ return (ch >= 0) && (ch <= lastASCII);
}
constexpr bool IsLowerCase(int ch) noexcept {
@@ -122,16 +140,14 @@ template <typename T>
constexpr T MakeUpperCase(T ch) noexcept {
if (ch < 'a' || ch > 'z')
return ch;
- else
- return ch - 'a' + 'A';
+ return ch - 'a' + 'A';
}
template <typename T>
constexpr T MakeLowerCase(T ch) noexcept {
if (ch < 'A' || ch > 'Z')
return ch;
- else
- return ch - 'A' + 'a';
+ return ch - 'A' + 'a';
}
int CompareCaseInsensitive(const char *a, const char *b) noexcept;
diff --git a/src/Document.cxx b/src/Document.cxx
index 458c1e93f..289fb6731 100644
--- a/src/Document.cxx
+++ b/src/Document.cxx
@@ -530,6 +530,13 @@ void SCI_METHOD Document::SetErrorStatus(int status) {
}
}
+void Document::CheckPosition(Sci::Position pos) const {
+ PLATFORM_ASSERT((pos >= 0) && (pos <= LengthNoExcept()));
+ if ((pos < 0) || (pos > LengthNoExcept())) {
+ throw Failure(Status::OutsideDocument);
+ }
+}
+
Sci_Position SCI_METHOD Document::LineFromPosition(Sci_Position pos) const {
return cb.LineFromPosition(pos);
}
@@ -1485,6 +1492,7 @@ Sci::Position Document::InsertString(Sci::Position position, const char *s, Sci:
if (insertLength <= 0) {
return 0;
}
+ CheckPosition(position);
CheckReadOnly(); // Application may change read only state here
if (cb.IsReadOnly()) {
return 0;
@@ -1907,8 +1915,7 @@ std::string Document::TransformLineEnds(const char *s, size_t len, EndOfLine eol
void Document::ConvertLineEnds(EndOfLine eolModeSet) {
UndoGroup ug(this);
- const Sci::Position length = Length();
- for (Sci::Position pos = 0; pos < length; pos++) {
+ for (Sci::Position pos = 0; pos < LengthNoExcept(); pos++) {
const char ch = cb.CharAt(pos);
if (ch == '\r') {
if (cb.CharAt(pos + 1) == '\n') {
@@ -2572,10 +2579,10 @@ bool SCI_METHOD Document::SetStyleFor(Sci_Position length, char style) {
return false;
}
enteredStyling++;
- const Sci::Position prevEndStyled = endStyled;
- if (cb.SetStyleFor(endStyled, length, style)) {
+ const ChangedRange cr = cb.SetStyleFor(endStyled, length, style);
+ if (!cr.Empty()) {
const DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User,
- prevEndStyled, length);
+ cr.start, cr.end - cr.start + 1);
NotifyModified(mh);
}
endStyled += length;
@@ -2588,22 +2595,11 @@ bool SCI_METHOD Document::SetStyles(Sci_Position length, const char *styles) {
return false;
}
enteredStyling++;
- bool didChange = false;
- Sci::Position startMod = 0;
- Sci::Position endMod = 0;
- for (int iPos = 0; iPos < length; iPos++, endStyled++) {
- PLATFORM_ASSERT(endStyled < Length());
- if (cb.SetStyleAt(endStyled, styles[iPos])) {
- if (!didChange) {
- startMod = endStyled;
- }
- didChange = true;
- endMod = endStyled;
- }
- }
- if (didChange) {
+ const ChangedRange cr = cb.SetStyles(endStyled, styles, length);
+ endStyled += length;
+ if (!cr.Empty()) {
const DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User,
- startMod, endMod - startMod + 1);
+ cr.start, cr.end - cr.start + 1);
NotifyModified(mh);
}
enteredStyling--;
@@ -3022,7 +3018,7 @@ Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxRe
return -1;
const int styBrace = StyleIndexAt(position);
int direction = -1;
- if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
+ if (AnyOf(chBrace, '(', '[', '{', '<'))
direction = 1;
int depth = 1;
position = useStartPos ? startPos : position + direction;
@@ -3035,7 +3031,7 @@ Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxRe
while ((position >= 0) && (position < LengthNoExcept())) {
const unsigned char chAtPos = CharAt(position);
- if (chAtPos == chBrace || chAtPos == chSeek) {
+ if (AnyOf(chAtPos, chBrace, chSeek)) {
if (((position > GetEndStyled()) || (StyleIndexAt(position) == styBrace)) &&
(chAtPos <= maxSafeChar || position == MovePositionOutsideChar(position, direction, false))) {
depth += (chAtPos == chBrace) ? 1 : -1;
@@ -3141,7 +3137,9 @@ public:
const Document *doc;
Sci::Position position;
- explicit ByteIterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
+ ByteIterator() noexcept :
+ ByteIterator(nullptr) {}
+ explicit ByteIterator(const Document *doc_, Sci::Position position_=0) noexcept :
doc(doc_), position(position_) {
}
char operator*() const noexcept {
@@ -3206,7 +3204,9 @@ public:
using pointer = wchar_t*;
using reference = wchar_t&;
- explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
+ UTF8Iterator() noexcept :
+ UTF8Iterator(nullptr) {}
+ explicit UTF8Iterator(const Document *doc_, Sci::Position position_=0) noexcept :
doc(doc_), position(position_) {
if (doc) {
ReadCharacter();
@@ -3295,7 +3295,9 @@ public:
using pointer = wchar_t*;
using reference = wchar_t&;
- explicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
+ UTF8Iterator() noexcept :
+ UTF8Iterator(nullptr) {}
+ explicit UTF8Iterator(const Document *doc_, Sci::Position position_=0) noexcept :
doc(doc_), position(position_) {
}
wchar_t operator*() const noexcept {
diff --git a/src/Document.h b/src/Document.h
index 7fd150f37..dfb0a7ff5 100644
--- a/src/Document.h
+++ b/src/Document.h
@@ -369,6 +369,7 @@ public:
int SCI_METHOD DEVersion() const noexcept override;
void SCI_METHOD SetErrorStatus(int status) override;
+ void CheckPosition(Sci::Position pos) const;
Sci_Position SCI_METHOD LineFromPosition(Sci_Position pos) const override;
Sci::Line SciLineFromPosition(Sci::Position pos) const noexcept; // Avoids casting LineFromPosition
diff --git a/src/EditView.cxx b/src/EditView.cxx
index 2608f11e5..b25053845 100644
--- a/src/EditView.cxx
+++ b/src/EditView.cxx
@@ -352,7 +352,7 @@ void LayoutSegments(IPositionCache *pCache,
XYPOSITION representationWidth = 0.0;
// Tab is a special case of representation, taking a variable amount of space
// which will be filled in later.
- if (ll->chars[ts.start] != '\t') {
+ if (ll->chars[ts.start] != '\t' || vstyle.tabDrawMode == TabDrawMode::ControlChar) {
representationWidth = vstyle.controlCharWidth;
if (representationWidth <= 0.0) {
assert(ts.representation->stringRep.length() <= Representation::maxLength);
@@ -522,7 +522,7 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt
for (const TextSegment &ts : segments) {
if (vstyle.styles[ll->styles[ts.start]].visible &&
ts.representation &&
- (ll->chars[ts.start] == '\t')) {
+ ll->chars[ts.start] == '\t' && vstyle.tabDrawMode != TabDrawMode::ControlChar) {
// Simple visible tab, go to next tab stop
const XYPOSITION startTab = ll->positions[ts.start];
const XYPOSITION nextTab = NextTabstopPos(line, startTab, vstyle.tabWidth);
@@ -610,7 +610,7 @@ void EditView::UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, L
const Representation *repr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[charsInLine], charWidth));
ll->bidiData->widthReprs[charsInLine] = 0.0f;
- if (repr && ll->chars[charsInLine] != '\t') {
+ if (repr && (ll->chars[charsInLine] != '\t' || vstyle.tabDrawMode == TabDrawMode::ControlChar)) {
ll->bidiData->widthReprs[charsInLine] = ll->positions[charsInLine + charWidth] - ll->positions[charsInLine];
}
if (charWidth > 1) {
@@ -1286,6 +1286,7 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c
// it may be double drawing. This is to allow stadiums with
// curved or angled ends to have the area outside in the correct
// background colour.
+ surface->FillRectangleAligned(rcSegment, Fill(textBack));
FillLineRemainder(surface, model, vsDraw, ll, line, rcLine, rcSegment.right, subLine);
}
@@ -1346,7 +1347,7 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c
namespace {
constexpr bool AnnotationBoxedOrIndented(AnnotationVisible annotationVisible) noexcept {
- return annotationVisible == AnnotationVisible::Boxed || annotationVisible == AnnotationVisible::Indented;
+ return AnyOf(annotationVisible, AnnotationVisible::Boxed, AnnotationVisible::Indented);
}
}
@@ -1673,7 +1674,7 @@ void DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &v
ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection,
inHotspot, ll->styles[i], i);
if (ts.representation) {
- if (ll->chars[i] == '\t') {
+ if (ll->chars[i] == '\t' && vsDraw.tabDrawMode != TabDrawMode::ControlChar) {
// Tab display
if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {
textBack = vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque();
@@ -1682,21 +1683,27 @@ void DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &v
// Blob display
inIndentation = false;
}
- surface->FillRectangleAligned(rcSegment, Fill(textBack));
- } else {
+ }
+ surface->FillRectangleAligned(rcSegment, Fill(textBack));
+ if (!ts.representation) {
// Normal text display
- surface->FillRectangleAligned(rcSegment, Fill(textBack));
if (vsDraw.viewWhitespace != WhiteSpace::Invisible) {
- for (int cpos = 0; cpos <= i - ts.start; cpos++) {
- if (ll->chars[cpos + ts.start] == ' ') {
+ for (int cpos = 0; cpos <= i - ts.start; ) {
+ int countSpaces = 0;
+ while ((countSpaces <= i - ts.start - cpos) && (ll->chars[cpos + ts.start + countSpaces] == ' ')) {
+ countSpaces++;
+ }
+ if (countSpaces) {
if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {
const PRectangle rcSpace = Intersection(rcLine,
- ll->SpanByte(cpos + ts.start).Offset(horizontalOffset));
+ ll->Span(cpos + ts.start, cpos + ts.start + countSpaces).Offset(horizontalOffset));
surface->FillRectangleAligned(rcSpace,
vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque());
}
+ cpos += countSpaces;
} else {
inIndentation = false;
+ cpos++;
}
}
}
@@ -2178,7 +2185,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi
}
ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);
if (ts.representation) {
- if (ll->chars[i] == '\t') {
+ if (ll->chars[i] == '\t' && vsDraw.tabDrawMode != TabDrawMode::ControlChar) {
// Tab display
if (phasesDraw == PhasesDraw::One) {
if (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))
@@ -2310,7 +2317,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi
void EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible) {
- if ((vsDraw.viewIndentationGuides == IndentView::LookForward || vsDraw.viewIndentationGuides == IndentView::LookBoth)
+ if (AnyOf(vsDraw.viewIndentationGuides, IndentView::LookForward, IndentView::LookBoth)
&& (subLine == 0)) {
const Sci::Position posLineStart = model.pdoc->LineStart(line);
int indentSpace = model.pdoc->GetLineIndentation(line);
@@ -2525,14 +2532,14 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
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) {
+ Sci::Line lineVisible = model.TopLineOfMain() + screenLinePaintFirst;
+ while (lineVisible < model.pcs->LinesDisplayed() && yposScreen < rcArea.bottom) {
- const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);
+ const Sci::Line lineDoc = model.pcs->DocFromDisplay(lineVisible);
// Only visible lines should be handled by the code within the loop
PLATFORM_ASSERT(model.pcs->GetVisible(lineDoc));
const Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);
- const int subLine = static_cast<int>(visibleLine - lineStartSet);
+ const int subLine = static_cast<int>(lineVisible - lineStartSet);
// Copy this line and its styles from the document into local arrays
// and determine the x position at which each character starts.
@@ -2574,7 +2581,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
surface->FillRectangleAligned(rcSpacer, Fill(vsDraw.styles[StyleDefault].back));
}
- DrawLine(surface, model, vsDraw, ll.get(), lineDoc, visibleLine, xOrigin, rcLine, subLine, phase);
+ DrawLine(surface, model, vsDraw, ll.get(), lineDoc, lineVisible, xOrigin, rcLine, subLine, phase);
#if defined(TIME_PAINTING)
durPaint += ep.Duration(true);
#endif
@@ -2609,7 +2616,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const V
}
yposScreen += vsDraw.lineHeight;
- visibleLine++;
+ lineVisible++;
}
if (phase >= DrawPhase::carets) {
@@ -2704,7 +2711,7 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl
} else if (colourMode == PrintOption::BlackOnWhite) {
it->fore = black;
it->back = white;
- } else if (colourMode == PrintOption::ColourOnWhite || colourMode == PrintOption::ColourOnWhiteDefaultBG) {
+ } else if (AnyOf(colourMode, PrintOption::ColourOnWhite, PrintOption::ColourOnWhiteDefaultBG)) {
it->back = white;
}
}
@@ -2756,7 +2763,7 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl
Sci::Line lineDoc = linePrintStart;
Sci::Position nPrintPos = chrg.cpMin;
- int visibleLine = 0;
+ int lineVisible = 0;
int widthPrint = rc.right - rc.left - vsPrint.fixedColumnWidth;
if (printParameters.wrapState == Wrap::None)
widthPrint = LineLayout::wrapWidthInfinite;
@@ -2785,23 +2792,23 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl
// When document line is wrapped over multiple display lines, find where
// to start printing from to ensure a particular position is on the first
// line of the page.
- if (visibleLine == 0) {
+ if (lineVisible == 0) {
const Sci::Position startWithinLine = nPrintPos -
model.pdoc->LineStart(lineDoc);
for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
- visibleLine = -iwl;
+ lineVisible = -iwl;
}
}
if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
- visibleLine = -(ll.lines - 1);
+ lineVisible = -(ll.lines - 1);
}
}
if (draw && lineNumberWidth &&
(ypos + vsPrint.lineHeight <= rc.bottom) &&
- (visibleLine >= 0)) {
+ (lineVisible >= 0)) {
const std::string number = std::to_string(lineDoc + 1) + lineNumberPrintSpace;
PRectangle rcNumber = rcLine;
rcNumber.right = rcNumber.left + lineNumberWidth;
@@ -2820,15 +2827,15 @@ Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangl
for (int iwl = 0; iwl < ll.lines; iwl++) {
if (ypos + vsPrint.lineHeight <= rc.bottom) {
- if (visibleLine >= 0) {
+ if (lineVisible >= 0) {
if (draw) {
rcLine.top = static_cast<XYPOSITION>(ypos);
rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
- DrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xOrigin, rcLine, iwl, DrawPhase::all);
+ DrawLine(surface, model, vsPrint, &ll, lineDoc, lineVisible, xOrigin, rcLine, iwl, DrawPhase::all);
}
ypos += vsPrint.lineHeight;
}
- visibleLine++;
+ lineVisible++;
if (iwl == ll.lines - 1)
nPrintPos = model.pdoc->LineStart(lineDoc + 1);
else
diff --git a/src/Editor.cxx b/src/Editor.cxx
index 5b42407e3..1dfa8fd6b 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -3818,7 +3818,7 @@ int Editor::DelWordOrLine(Message iMessage) {
// Rightwards and leftwards deletions differ in treatment of virtual space.
// Clear virtual space for leftwards, realise for rightwards.
- const bool leftwards = (iMessage == Message::DelWordLeft) || (iMessage == Message::DelLineLeft);
+ const bool leftwards = AnyOf(iMessage, Message::DelWordLeft, Message::DelLineLeft);
if (!additionalSelectionTyping) {
InvalidateWholeSelection();
@@ -4029,14 +4029,14 @@ int Editor::KeyCommand(Message iMessage) {
break;
case Message::DeleteBack:
DelCharBack(true);
- if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
+ if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) {
SetLastXChosen();
}
EnsureCaretVisible();
break;
case Message::DeleteBackNotLine:
DelCharBack(false);
- if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
+ if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) {
SetLastXChosen();
}
EnsureCaretVisible();
@@ -4053,7 +4053,7 @@ int Editor::KeyCommand(Message iMessage) {
case Message::BackTab:
case Message::LineDedent:
Indent(false, iMessage == Message::LineDedent);
- if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
+ if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) {
SetLastXChosen();
}
EnsureCaretVisible();
@@ -4859,7 +4859,7 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modi
wordSelectAnchorEndPos = endWord;
wordSelectInitialCaretPos = sel.MainCaret();
WordSelection(wordSelectInitialCaretPos);
- } else if (selectionUnit == TextUnit::subLine || selectionUnit == TextUnit::wholeLine) {
+ } else if (AnyOf(selectionUnit, TextUnit::subLine, TextUnit::wholeLine)) {
lineAnchorPos = newPos.Position();
LineSelection(lineAnchorPos, lineAnchorPos, selectionUnit == TextUnit::wholeLine);
//Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
@@ -4919,7 +4919,7 @@ void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modi
// Switch to just the click position
SetSelection(newPos, newPos);
}
- if (!sel.Range(selectionPart).Empty()) {
+ if (dragDropEnabled && !sel.Range(selectionPart).Empty()) {
inDragDrop = DragDrop::initial;
}
}
@@ -5395,7 +5395,7 @@ Sci::Position Editor::PositionAfterMaxStyling(Sci::Position posMax, bool scrolli
}
void Editor::StartIdleStyling(bool truncatedLastStyling) {
- if ((idleStyling == IdleStyling::All) || (idleStyling == IdleStyling::AfterVisible)) {
+ if (AnyOf(idleStyling, IdleStyling::All, IdleStyling::AfterVisible)) {
if (pdoc->GetEndStyled() < pdoc->Length()) {
// Style remainder of document in idle time
needIdleStyling = true;
@@ -5899,6 +5899,8 @@ Sci::Position Editor::GetTag(char *tagValue, int tagNumber) {
}
Sci::Position Editor::ReplaceTarget(ReplaceType replaceType, std::string_view text) {
+ pdoc->CheckPosition(targetRange.start.Position());
+
UndoGroup ug(pdoc);
std::string substituted; // Copy in case of re-entrance
@@ -6344,7 +6346,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case Message::Paste:
Paste();
- if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
+ if (AnyOf(caretSticky, CaretSticky::Off, CaretSticky::WhiteSpace)) {
SetLastXChosen();
}
EnsureCaretVisible();
@@ -6697,6 +6699,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
Sci::Position insertPos = PositionFromUPtr(wParam);
if (insertPos == -1)
insertPos = CurrentPosition();
+ pdoc->CheckPosition(insertPos);
Sci::Position newCurrent = CurrentPosition();
const char *sz = ConstCharPtrFromSPtr(lParam);
const Sci::Position lengthInserted = pdoc->InsertString(insertPos, sz, strlen(sz));
@@ -6984,7 +6987,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
return static_cast<sptr_t>(vs.tabDrawMode);
case Message::SetTabDrawMode:
- vs.tabDrawMode = static_cast<TabDrawMode>(wParam);
+ SetAppearance(vs.tabDrawMode, static_cast<TabDrawMode>(wParam));
Redraw();
break;
diff --git a/src/Editor.h b/src/Editor.h
index 95b83f1f7..0243d5b63 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -16,7 +16,7 @@ class Timer {
public:
bool ticking;
int ticksToWait;
- enum {tickSize = 100};
+ static constexpr int tickSize = 100;
TickerID tickerID;
Timer() noexcept;
@@ -226,7 +226,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
Timer timer;
Timer autoScrollTimer;
- enum { autoScrollDelay = 200 };
+ static constexpr int autoScrollDelay = 200;
Idler idler;
diff --git a/test/simpleTests.py b/test/simpleTests.py
index 0a14a4bdb..4205c7b3a 100644
--- a/test/simpleTests.py
+++ b/test/simpleTests.py
@@ -371,6 +371,12 @@ class TestSimple(unittest.TestCase):
self.assertEqual(self.ed.Contents(), b"x" + lineEnds[lineEndType] + b"y")
self.assertEqual(self.ed.LineLength(0), 1 + len(lineEnds[lineEndType]))
+ def testLineEndConversionLengthening(self):
+ # Bug #2501
+ self.ed.AddText(4, b"x\ny\n")
+ self.ed.ConvertEOLs(self.ed.SC_EOL_CRLF)
+ self.assertEqual(self.ed.Contents(), b"x\r\ny\r\n")
+
# Several tests for unicode line ends U+2028 and U+2029
@unittest.skipUnless(unicodeLineEndsAvailable, "can not test Unicode line ends")
diff --git a/test/unit/testDocument.cxx b/test/unit/testDocument.cxx
index fb8448da4..9811a215c 100644
--- a/test/unit/testDocument.cxx
+++ b/test/unit/testDocument.cxx
@@ -157,8 +157,30 @@ struct DocPlus {
document.GetCharRange(contents.data(), 0, length);
return contents;
}
+
+ [[nodiscard]] std::string Styles() const {
+ const Sci::Position length = document.Length();
+ std::string contents(length, 0);
+ document.GetStyleRange(reinterpret_cast<unsigned char *>(contents.data()), 0, length);
+ return contents;
+ }
+};
+
+namespace {
+
+// Provide an empty watcher to avoid filling in all the virtuals in simple watchers.
+struct EmptyWatcher : public DocWatcher {
+ void NotifyModifyAttempt(Document *, void *) override {};
+ void NotifySavePoint(Document *, void *, bool) override {};
+ void NotifyModified(Document *, DocModification, void *) override {};
+ void NotifyDeleted(Document *, void *) noexcept override {};
+ void NotifyStyleNeeded(Document *, void *, Sci::Position) override {};
+ void NotifyErrorOccurred(Document *, void *, Scintilla::Status) override {};
+ void NotifyGroupCompleted(Document *, void *) noexcept override {};
};
+}
+
void TimeTrace(std::string_view sv, const Catch::Timer &tikka) {
std::cout << sv << std::setw(5) << tikka.getElapsedMilliseconds() << " milliseconds" << std::endl;
}
@@ -185,6 +207,81 @@ TEST_CASE("Document") {
REQUIRE(!doc.document.CanRedo());
}
+ SECTION("Style") {
+ // Check that styling calls SetStyles and SetStyleFor modify the correct bytes and
+ // produce a minimal span in the notification.
+ // Also checks the notification mechanism.
+ struct ModificationWatcher : public EmptyWatcher {
+ Sci::Position modPos = -1;
+ Sci::Position modLength = -1;
+ void NotifyModified(Document *, DocModification mh, void *) noexcept final {
+ modPos = mh.position;
+ modLength = mh.length;
+ }
+ void Clear() noexcept {
+ modPos = -1;
+ modLength = -1;
+ }
+ };
+ DocPlus doc(sText, 0);
+ // Length of sText is 9
+ REQUIRE(doc.Styles() == std::string(9, 0));
+ ModificationWatcher mw;
+ doc.document.AddWatcher(&mw, nullptr);
+ doc.document.StartStyling(1);
+ doc.document.SetStyles(5, "\0abc\0");
+ REQUIRE(doc.Styles() == std::string("\0\0abc\0\0\0\0", 9));
+ // The NULs are not changed so are not included in the modification range
+ REQUIRE(mw.modPos == 2);
+ REQUIRE(mw.modLength == 3);
+
+ // No change so no notification so no update
+ mw.Clear();
+ doc.document.StartStyling(1);
+ doc.document.SetStyles(5, "\0abc\0");
+ REQUIRE(mw.modPos == -1);
+ REQUIRE(mw.modLength == -1);
+
+ // Just change one byte
+ doc.document.StartStyling(1);
+ doc.document.SetStyles(5, "\0bbc\0");
+ REQUIRE(mw.modPos == 2);
+ REQUIRE(mw.modLength == 1);
+ REQUIRE(doc.Styles() == std::string("\0\0bbc\0\0\0\0", 9));
+
+ // Move the gap
+ doc.MoveGap(1);
+ doc.document.StartStyling(1);
+ doc.document.SetStyles(5, "\0cbc\0");
+ REQUIRE(mw.modPos == 2);
+ REQUIRE(mw.modLength == 1);
+ REQUIRE(doc.Styles() == std::string("\0\0cbc\0\0\0\0", 9));
+
+ doc.MoveGap(2);
+ doc.document.StartStyling(1);
+ doc.document.SetStyles(5, "\0dcc\0");
+ REQUIRE(mw.modPos == 2);
+ REQUIRE(mw.modLength == 2);
+ REQUIRE(doc.Styles() == std::string("\0\0dcc\0\0\0\0", 9));
+
+ // Test SetStyleFor
+
+ doc.MoveGap(3);
+ doc.document.StartStyling(3);
+ doc.document.SetStyleFor(1, 0); // After gap
+ REQUIRE(mw.modPos == 3);
+ REQUIRE(mw.modLength == 1);
+ REQUIRE(doc.Styles() == std::string("\0\0d\0c\0\0\0\0", 9));
+
+ doc.document.StartStyling(2);
+ doc.document.SetStyleFor(3, 0); // Over gap
+ REQUIRE(mw.modPos == 2);
+ REQUIRE(mw.modLength == 3);
+ REQUIRE(doc.Styles() == std::string("\0\0\0\0\0\0\0\0\0", 9));
+
+ doc.document.RemoveWatcher(&mw, nullptr);
+ }
+
// Search ranges are from first argument to just before second argument
// Arguments are expected to be at character boundaries and will be tweaked if
// part way through a character.
diff --git a/version.txt b/version.txt
index c7884c020..3bfc51a6e 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-560
+562
diff --git a/win32/ListBox.cxx b/win32/ListBox.cxx
index b5ce7782f..31470b2ed 100644
--- a/win32/ListBox.cxx
+++ b/win32/ListBox.cxx
@@ -175,6 +175,10 @@ class ListBoxX : public ListBox {
MouseWheelDelta wheelDelta;
ListOptions options;
DWORD frameStyle = WS_THICKFRAME;
+ float deviceScaleFactor = 1.f;
+ [[nodiscard]] int GetFirstIntegralMultipleDeviceScaleFactor() const noexcept {
+ return static_cast<int>(std::ceil(deviceScaleFactor));
+ }
LBGraphics graphics;
@@ -265,6 +269,9 @@ void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHei
this);
dpi = DpiForWindow(hwndParent);
+ if (technology != Technology::Default) {
+ deviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hwndParent);
+ }
POINT locationw = POINTFromPoint(location);
::MapWindowPoints(hwndParent, {}, &locationw, 1);
location = PointFromPOINT(locationw);
@@ -423,7 +430,8 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
#endif
const PRectangle rcItemBase = PRectangleFromRECT(pDrawItem->rcItem);
- const PRectangle rcItem(0, 0, rcItemBase.Width(), rcItemBase.Height());
+ const int integralDeviceScaleFactor = GetFirstIntegralMultipleDeviceScaleFactor();
+ const PRectangle rcItem(0, 0, rcItemBase.Width() * integralDeviceScaleFactor, rcItemBase.Height() * integralDeviceScaleFactor);
PRectangle rcBox = rcItem;
rcBox.left += TextOffset();
ColourRGBA colourFore;
@@ -458,6 +466,7 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
PRectangle rcImage = rcItem;
rcImage.left = left;
rcImage.right = rcImage.left + images.GetWidth();
+ rcImage.bottom = rcImage.top + rcItemBase.Height();
graphics.pixmapLine->DrawRGBAImage(rcImage,
pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());
}
@@ -473,7 +482,11 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
// Blit from hMemDC to hDC
const SIZE extent = SizeOfRect(pDrawItem->rcItem);
- ::BitBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, SRCCOPY);
+ if (integralDeviceScaleFactor == 1) {
+ ::BitBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, SRCCOPY);
+ } else {
+ ::StretchBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, extent.cx * integralDeviceScaleFactor, extent.cy * integralDeviceScaleFactor, SRCCOPY);
+ }
}
void ListBoxX::AppendListItem(const char *text, const char *numword) {
@@ -748,7 +761,8 @@ void ListBoxX::CentreItem(int n) {
}
void ListBoxX::AllocateBitMap() {
- const SIZE extent { GetClientExtent().x, ItemHeight() };
+ const int integralDeviceScaleFactor = GetFirstIntegralMultipleDeviceScaleFactor();
+ const SIZE extent { GetClientExtent().x * integralDeviceScaleFactor, ItemHeight() * integralDeviceScaleFactor };
graphics.bm.Create({}, extent.cx, -extent.cy, nullptr);
if (!graphics.bm) {
@@ -765,9 +779,12 @@ void ListBoxX::AllocateBitMap() {
return;
}
+ const FLOAT dpiTarget = dpiDefault * static_cast<float>(integralDeviceScaleFactor);
+
const D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
- { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED });
+ { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED },
+ dpiTarget, dpiTarget);
HRESULT hr = CreateDCRenderTarget(&drtp, graphics.pBMDCTarget);
if (FAILED(hr) || !graphics.pBMDCTarget) {
diff --git a/win32/ScintRes.rc b/win32/ScintRes.rc
index 007b0ead9..46526cec8 100644
--- a/win32/ScintRes.rc
+++ b/win32/ScintRes.rc
@@ -4,8 +4,8 @@
#include <windows.h>
-#define VERSION_SCINTILLA "5.6.0"
-#define VERSION_WORDS 5, 6, 0, 0
+#define VERSION_SCINTILLA "5.6.2"
+#define VERSION_WORDS 5, 6, 2, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_WORDS
diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx
index f59a5e28e..856c66ca9 100644
--- a/win32/ScintillaWin.cxx
+++ b/win32/ScintillaWin.cxx
@@ -333,9 +333,9 @@ public:
void SetCompositionFont(const ViewStyle &vs, int style, UINT dpi) const {
LOGFONTW lf{};
- int sizeZoomed = vs.styles[style].size + (vs.zoomLevel * FontSizeMultiplier);
- if (sizeZoomed <= 2 * FontSizeMultiplier) // Hangs if sizeZoomed <= 1
- sizeZoomed = 2 * FontSizeMultiplier;
+ // Hangs if font height <= 1, so force minimum value
+ const int sizeZoomed = std::max(vs.styles[style].size + (vs.zoomLevel * FontSizeMultiplier),
+ 2 * FontSizeMultiplier);
// The negative is to allow for leading
lf.lfHeight = -::MulDiv(sizeZoomed, dpi, pointsPerInch * FontSizeMultiplier);
lf.lfWeight = static_cast<LONG>(vs.styles[style].weight);
@@ -872,7 +872,11 @@ bool ScintillaWin::UpdateRenderingParams(bool force) noexcept {
}
hCurrentMonitor = monitor;
- deviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hRootWnd);
+ const float newDeviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hRootWnd);
+ if (deviceScaleFactor != newDeviceScaleFactor) {
+ deviceScaleFactor = newDeviceScaleFactor;
+ targets.valid = false;
+ }
renderingParams->defaultRenderingParams = std::move(monitorRenderingParams);
renderingParams->customRenderingParams = std::move(customClearTypeRenderingParams);
return true;
@@ -2434,17 +2438,18 @@ sptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case WM_MOUSEACTIVATE:
case WM_NCHITTEST:
case WM_NCCALCSIZE:
- case WM_NCPAINT:
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
case WM_SYSCOMMAND:
case WM_WINDOWPOSCHANGING:
return ::DefWindowProc(MainHWND(), msg, wParam, lParam);
+ case WM_NCPAINT:
case WM_WINDOWPOSCHANGED:
#if defined(USE_D2D)
if (technology != Technology::Default) {
if (UpdateRenderingParams(false)) {
+ reverseArrowCursor.Invalidate();
DropGraphics();
Redraw();
}
@@ -2495,6 +2500,8 @@ sptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
}
} catch (std::bad_alloc &) {
errorStatus = Status::BadAlloc;
+ } catch (Failure &failure) {
+ errorStatus = failure.status;
} catch (...) {
errorStatus = Status::Failure;
}