From cf1f524c193c063c95f86efe9b908b6b311f86dd Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Thu, 15 Sep 2022 10:52:23 +0500 Subject: [PATCH 1/6] Update Lexilla and Scintilla: * Lexilla to version 5.19 * Scintilla to version 5.30 --- meson.build | 3 +- scintilla/Makefile.am | 4 +- scintilla/gtk/Converter.h | 7 +- scintilla/gtk/PlatGTK.cxx | 492 +++++++++++---------- scintilla/gtk/ScintillaGTK.cxx | 208 ++++----- scintilla/gtk/ScintillaGTK.h | 6 +- scintilla/gtk/ScintillaGTKAccessible.cxx | 9 +- scintilla/gtk/Wrappers.h | 109 +++++ scintilla/include/Scintilla.h | 55 ++- scintilla/include/Scintilla.iface | 65 ++- scintilla/include/ScintillaCall.h | 10 + scintilla/include/ScintillaMessages.h | 9 + scintilla/include/ScintillaStructures.h | 26 ++ scintilla/include/ScintillaTypes.h | 34 +- scintilla/lexilla/include/SciLexer.h | 1 + scintilla/lexilla/lexers/LexBash.cxx | 8 +- scintilla/lexilla/lexers/LexBatch.cxx | 9 +- scintilla/lexilla/lexers/LexCPP.cxx | 109 +++-- scintilla/lexilla/lexers/LexCmake.cxx | 2 +- scintilla/lexilla/lexers/LexGDScript.cxx | 1 - scintilla/lexilla/lexers/LexHTML.cxx | 59 +-- scintilla/lexilla/lexers/LexJulia.cxx | 4 +- scintilla/lexilla/lexers/LexMarkdown.cxx | 120 +++-- scintilla/lexilla/lexers/LexMatlab.cxx | 168 +++++-- scintilla/lexilla/lexers/LexPowerShell.cxx | 36 +- scintilla/lexilla/lexers/LexProps.cxx | 39 +- scintilla/lexilla/lexers/LexPython.cxx | 48 ++ scintilla/lexilla/lexers/LexR.cxx | 108 +++-- scintilla/lexilla/lexers/LexRuby.cxx | 191 ++++++-- scintilla/lexilla/lexers/LexVHDL.cxx | 18 +- scintilla/lexilla/lexlib/LexAccessor.cxx | 9 +- scintilla/lexilla/lexlib/LexAccessor.h | 16 + scintilla/lexilla/lexlib/OptionSet.h | 2 + scintilla/lexilla/lexlib/PropSetSimple.cxx | 2 + scintilla/lexilla/lexlib/StyleContext.cxx | 28 ++ scintilla/lexilla/lexlib/StyleContext.h | 66 +-- scintilla/lexilla/lexlib/WordList.cxx | 1 + scintilla/lexilla/version.txt | 2 +- scintilla/src/CallTip.cxx | 18 +- scintilla/src/CallTip.h | 6 +- scintilla/src/CaseFolder.cxx | 34 +- scintilla/src/CaseFolder.h | 8 +- scintilla/src/CellBuffer.cxx | 182 ++++++-- scintilla/src/CellBuffer.h | 33 +- scintilla/src/ChangeHistory.cxx | 422 ++++++++++++++++++ scintilla/src/ChangeHistory.h | 112 +++++ scintilla/src/ContractionState.cxx | 83 ++-- scintilla/src/ContractionState.h | 1 + scintilla/src/Decoration.cxx | 48 +- scintilla/src/Decoration.h | 2 +- scintilla/src/Document.cxx | 55 ++- scintilla/src/Document.h | 10 +- scintilla/src/EditModel.cxx | 8 + scintilla/src/EditModel.h | 6 +- scintilla/src/EditView.cxx | 375 ++++++++++------ scintilla/src/EditView.h | 13 +- scintilla/src/Editor.cxx | 292 ++++++++---- scintilla/src/Editor.h | 45 +- scintilla/src/FontQuality.h | 27 -- scintilla/src/Geometry.cxx | 20 +- scintilla/src/Geometry.h | 1 + scintilla/src/Indicator.cxx | 20 +- scintilla/src/KeyMap.cxx | 4 +- scintilla/src/KeyMap.h | 2 + scintilla/src/LineMarker.cxx | 31 ++ scintilla/src/MarginView.cxx | 24 +- scintilla/src/Partitioning.h | 128 +++--- scintilla/src/PerLine.cxx | 36 +- scintilla/src/PerLine.h | 40 +- scintilla/src/Platform.h | 2 +- scintilla/src/PositionCache.cxx | 94 +++- scintilla/src/PositionCache.h | 50 +-- scintilla/src/RESearch.cxx | 31 +- scintilla/src/RunStyles.cxx | 118 +++-- scintilla/src/RunStyles.h | 10 +- scintilla/src/ScintillaBase.cxx | 16 +- scintilla/src/ScintillaBase.h | 1 - scintilla/src/Selection.h | 4 +- scintilla/src/SparseVector.h | 143 +++--- scintilla/src/SplitVector.h | 28 +- scintilla/src/UniConversion.cxx | 4 +- scintilla/src/UniConversion.h | 2 +- scintilla/src/UniqueString.cxx | 2 - scintilla/src/UniqueString.h | 7 - scintilla/src/ViewStyle.cxx | 74 +++- scintilla/src/ViewStyle.h | 3 + scintilla/version.txt | 2 +- 87 files changed, 3201 insertions(+), 1560 deletions(-) create mode 100644 scintilla/gtk/Wrappers.h create mode 100644 scintilla/src/ChangeHistory.cxx create mode 100644 scintilla/src/ChangeHistory.h delete mode 100644 scintilla/src/FontQuality.h diff --git a/meson.build b/meson.build index 4b50facdda..0f4300fe86 100644 --- a/meson.build +++ b/meson.build @@ -365,6 +365,8 @@ scintilla = static_library('scintilla', 'scintilla/src/CaseFolder.h', 'scintilla/src/CellBuffer.cxx', 'scintilla/src/CellBuffer.h', + 'scintilla/src/ChangeHistory.cxx', + 'scintilla/src/ChangeHistory.h', 'scintilla/src/CharacterCategoryMap.cxx', 'scintilla/src/CharacterCategoryMap.h', 'scintilla/src/CharacterType.cxx', @@ -387,7 +389,6 @@ scintilla = static_library('scintilla', 'scintilla/src/EditView.cxx', 'scintilla/src/EditView.h', 'scintilla/src/ElapsedPeriod.h', - 'scintilla/src/FontQuality.h', 'scintilla/src/Geometry.cxx', 'scintilla/src/Geometry.h', 'scintilla/src/Indicator.cxx', diff --git a/scintilla/Makefile.am b/scintilla/Makefile.am index cf13dd69a9..6bbe11ae0d 100644 --- a/scintilla/Makefile.am +++ b/scintilla/Makefile.am @@ -109,6 +109,7 @@ gtk/ScintillaGTKAccessible.cxx \ gtk/ScintillaGTKAccessible.h \ gtk/scintilla-marshal.c \ gtk/scintilla-marshal.h \ +gtk/Wrappers.h \ src/AutoComplete.cxx \ src/AutoComplete.h \ src/CallTip.cxx \ @@ -119,6 +120,8 @@ src/CaseFolder.cxx \ src/CaseFolder.h \ src/CellBuffer.cxx \ src/CellBuffer.h \ +src/ChangeHistory.cxx \ +src/ChangeHistory.h \ src/CharacterCategoryMap.cxx \ src/CharacterCategoryMap.h \ src/CharacterType.cxx \ @@ -141,7 +144,6 @@ src/Editor.h \ src/EditView.cxx \ src/EditView.h \ src/ElapsedPeriod.h \ -src/FontQuality.h \ src/Geometry.cxx \ src/Geometry.h \ src/Indicator.cxx \ diff --git a/scintilla/gtk/Converter.h b/scintilla/gtk/Converter.h index 30bb781ac0..0b95701f8a 100644 --- a/scintilla/gtk/Converter.h +++ b/scintilla/gtk/Converter.h @@ -14,7 +14,7 @@ const gsize sizeFailure = static_cast(-1); * Encapsulate g_iconv safely. */ class Converter { - GIConv iconvh; + GIConv iconvh = iconvhBad; void OpenHandle(const char *fullDestination, const char *charSetSource) noexcept { iconvh = g_iconv_open(fullDestination, charSetSource); } @@ -22,11 +22,8 @@ class Converter { return iconvh != iconvhBad; } public: - Converter() noexcept { - iconvh = iconvhBad; - } + Converter() noexcept = default; Converter(const char *charSetDestination, const char *charSetSource, bool transliterations) { - iconvh = iconvhBad; Open(charSetDestination, charSetSource, transliterations); } // Deleted so Converter objects can not be copied. diff --git a/scintilla/gtk/PlatGTK.cxx b/scintilla/gtk/PlatGTK.cxx index 97b345bac3..e73e4678d8 100644 --- a/scintilla/gtk/PlatGTK.cxx +++ b/scintilla/gtk/PlatGTK.cxx @@ -39,6 +39,7 @@ #include "XPM.h" #include "UniConversion.h" +#include "Wrappers.h" #include "Converter.h" #ifdef _MSC_VER @@ -55,17 +56,6 @@ constexpr double kPi = 3.14159265358979323846; constexpr double degrees = kPi / 180.0; -// The Pango version guard for pango_units_from_double and pango_units_to_double -// is more complex than simply implementing these here. - -constexpr int pangoUnitsFromDouble(double d) noexcept { - return static_cast(d * PANGO_SCALE + 0.5); -} - -constexpr float floatFromPangoUnits(int pu) noexcept { - return static_cast(pu) / PANGO_SCALE; -} - struct IntegerRectangle { int left; int top; @@ -80,48 +70,38 @@ struct IntegerRectangle { int Height() const noexcept { return bottom - top; } }; -GdkWindow *WindowFromWidget(GtkWidget *w) noexcept { - return gtk_widget_get_window(w); -} - GtkWidget *PWidget(WindowID wid) noexcept { return static_cast(wid); } +void SetFractionalPositions([[maybe_unused]] PangoContext *pcontext) noexcept { +#if PANGO_VERSION_CHECK(1,44,3) + pango_context_set_round_glyph_positions(pcontext, FALSE); +#endif +} + +void LayoutSetText(PangoLayout *layout, std::string_view text) noexcept { + pango_layout_set_text(layout, text.data(), static_cast(text.length())); +} + enum class EncodingType { singleByte, utf8, dbcs }; // Holds a PangoFontDescription*. class FontHandle : public Font { public: - PangoFontDescription *pfd = nullptr; + UniquePangoFontDescription fd; CharacterSet characterSet; - FontHandle() noexcept : pfd(nullptr), characterSet(CharacterSet::Ansi) { - } - FontHandle(PangoFontDescription *pfd_, CharacterSet characterSet_) noexcept { - pfd = pfd_; - characterSet = characterSet_; - } - FontHandle(const FontParameters &fp) { - pfd = pango_font_description_new(); - if (pfd) { - pango_font_description_set_family(pfd, + explicit FontHandle(const FontParameters &fp) : + fd(pango_font_description_new()), characterSet(fp.characterSet) { + if (fd) { + pango_font_description_set_family(fd.get(), (fp.faceName[0] == '!') ? fp.faceName + 1 : fp.faceName); - pango_font_description_set_size(pfd, pangoUnitsFromDouble(fp.size)); - pango_font_description_set_weight(pfd, static_cast(fp.weight)); - pango_font_description_set_style(pfd, fp.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); + pango_font_description_set_size(fd.get(), pango_units_from_double(fp.size)); + pango_font_description_set_weight(fd.get(), static_cast(fp.weight)); + pango_font_description_set_style(fd.get(), fp.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); } - characterSet = fp.characterSet; - } - // Deleted so FontHandle objects can not be copied. - FontHandle(const FontHandle &) = delete; - FontHandle(FontHandle &&) = delete; - FontHandle &operator=(const FontHandle &) = delete; - FontHandle &operator=(FontHandle &&) = delete; - ~FontHandle() override { - if (pfd) - pango_font_description_free(pfd); - pfd = nullptr; } + ~FontHandle() override = default; }; // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping @@ -137,7 +117,6 @@ std::shared_ptr Font::Allocate(const FontParameters &fp) { return std::make_shared(fp); } -// Required on OS X namespace Scintilla { // SurfaceID is a cairo_t* @@ -146,13 +125,18 @@ class SurfaceImpl : public Surface { EncodingType et= EncodingType::singleByte; WindowID widSave = nullptr; cairo_t *context = nullptr; - cairo_surface_t *psurf = nullptr; + UniqueCairo cairoOwned; + UniqueCairoSurface surf; bool inited = false; - bool createdGC = false; - PangoContext *pcontext = nullptr; - PangoLayout *layout = nullptr; + UniquePangoContext pcontext; + double resolution = 1.0; + PangoDirection direction = PANGO_DIRECTION_LTR; + const cairo_font_options_t *fontOptions = nullptr; + PangoLanguage *language = nullptr; + UniquePangoLayout layout; Converter conv; CharacterSet characterSet = static_cast(-1); + void PenColourAlpha(ColourRGBA fore) noexcept; void SetConverter(CharacterSet characterSet_); void CairoRectangle(PRectangle rc) noexcept; @@ -164,7 +148,10 @@ class SurfaceImpl : public Surface { SurfaceImpl(SurfaceImpl&&) = delete; SurfaceImpl&operator=(const SurfaceImpl&) = delete; SurfaceImpl&operator=(SurfaceImpl&&) = delete; - ~SurfaceImpl() override; + ~SurfaceImpl() override = default; + + void GetContextState() noexcept; + UniquePangoContext MeasuringContext(); void Init(WindowID wid) override; void Init(SurfaceID sid, WindowID wid) override; @@ -172,7 +159,6 @@ class SurfaceImpl : public Surface { void SetMode(SurfaceMode mode_) override; - void Clear() noexcept; void Release() noexcept override; int SupportsFeature(Supports feature) noexcept override; bool Initialised() override; @@ -228,6 +214,7 @@ const Supports SupportsGTK[] = { Supports::FractionalStrokeWidth, Supports::TranslucentStroke, Supports::PixelModification, + Supports::ThreadSafeMeasureWidths, }; } @@ -310,52 +297,39 @@ SurfaceImpl::SurfaceImpl() noexcept { SurfaceImpl::SurfaceImpl(cairo_t *context_, int width, int height, SurfaceMode mode_, WindowID wid) noexcept { if (height > 0 && width > 0) { cairo_surface_t *psurfContext = cairo_get_target(context_); - psurf = cairo_surface_create_similar( + surf.reset(cairo_surface_create_similar( psurfContext, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - context = cairo_create(psurf); - pcontext = gtk_widget_create_pango_context(PWidget(wid)); + CAIRO_CONTENT_COLOR_ALPHA, width, height)); + cairoOwned.reset(cairo_create(surf.get())); + context = cairoOwned.get(); + pcontext.reset(gtk_widget_create_pango_context(PWidget(wid))); PLATFORM_ASSERT(pcontext); - layout = pango_layout_new(pcontext); + SetFractionalPositions(pcontext.get()); + GetContextState(); + layout.reset(pango_layout_new(pcontext.get())); PLATFORM_ASSERT(layout); cairo_rectangle(context, 0, 0, width, height); cairo_set_source_rgb(context, 1.0, 0, 0); cairo_fill(context); cairo_set_line_width(context, 1); - createdGC = true; inited = true; mode = mode_; } } -SurfaceImpl::~SurfaceImpl() { - Clear(); -} - -void SurfaceImpl::Clear() noexcept { +void SurfaceImpl::Release() noexcept { et = EncodingType::singleByte; - if (createdGC) { - createdGC = false; - cairo_destroy(context); - } + cairoOwned.reset(); context = nullptr; - if (psurf) - cairo_surface_destroy(psurf); - psurf = nullptr; - if (layout) - g_object_unref(layout); - layout = nullptr; - if (pcontext) - g_object_unref(pcontext); - pcontext = nullptr; + surf.reset(); + layout.reset(); + // fontOptions and language are owned by original context and don't need to be freed + fontOptions = nullptr; + language = nullptr; + pcontext.reset(); conv.Close(); characterSet = static_cast(-1); inited = false; - createdGC = false; -} - -void SurfaceImpl::Release() noexcept { - Clear(); } bool SurfaceImpl::Initialised() { @@ -378,17 +352,39 @@ bool SurfaceImpl::Initialised() { return inited; } +void SurfaceImpl::GetContextState() noexcept { + resolution = pango_cairo_context_get_resolution(pcontext.get()); + direction = pango_context_get_base_dir(pcontext.get()); + fontOptions = pango_cairo_context_get_font_options(pcontext.get()); + language = pango_context_get_language(pcontext.get()); +} + +UniquePangoContext SurfaceImpl::MeasuringContext() { + UniquePangoFontMap fmMeasure(pango_cairo_font_map_get_default()); + PLATFORM_ASSERT(fmMeasure); + UniquePangoContext contextMeasure(pango_font_map_create_context(fmMeasure.release())); + PLATFORM_ASSERT(contextMeasure); + SetFractionalPositions(contextMeasure.get()); + + pango_cairo_context_set_resolution(contextMeasure.get(), resolution); + pango_context_set_base_dir(contextMeasure.get(), direction); + pango_cairo_context_set_font_options(contextMeasure.get(), fontOptions); + pango_context_set_language(contextMeasure.get(), language); + + return contextMeasure; +} + void SurfaceImpl::Init(WindowID wid) { widSave = wid; Release(); PLATFORM_ASSERT(wid); // if we are only created from a window ID, we can't perform drawing - psurf = nullptr; context = nullptr; - createdGC = false; - pcontext = gtk_widget_create_pango_context(PWidget(wid)); + pcontext.reset(gtk_widget_create_pango_context(PWidget(wid))); PLATFORM_ASSERT(pcontext); - layout = pango_layout_new(pcontext); + SetFractionalPositions(pcontext.get()); + GetContextState(); + layout.reset(pango_layout_new(pcontext.get())); PLATFORM_ASSERT(layout); inited = true; } @@ -398,13 +394,15 @@ void SurfaceImpl::Init(SurfaceID sid, WindowID wid) { PLATFORM_ASSERT(sid); Release(); PLATFORM_ASSERT(wid); - context = cairo_reference(static_cast(sid)); - pcontext = gtk_widget_create_pango_context(PWidget(wid)); + cairoOwned.reset(cairo_reference(static_cast(sid))); + context = cairoOwned.get(); + pcontext.reset(gtk_widget_create_pango_context(PWidget(wid))); + SetFractionalPositions(pcontext.get()); // update the Pango context in case sid isn't the widget's surface - pango_cairo_update_context(context, pcontext); - layout = pango_layout_new(pcontext); + pango_cairo_update_context(context, pcontext.get()); + GetContextState(); + layout.reset(pango_layout_new(pcontext.get())); cairo_set_line_width(context, 1); - createdGC = true; inited = true; } @@ -519,9 +517,9 @@ void SurfaceImpl::FillRectangleAligned(PRectangle rc, Fill fill) { void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { SurfaceImpl &surfi = dynamic_cast(surfacePattern); - if (context && surfi.psurf) { + if (context && surfi.surf) { // Tile pattern over rectangle - cairo_set_source_surface(context, surfi.psurf, rc.left, rc.top); + cairo_set_source_surface(context, surfi.surf.get(), rc.left, rc.top); cairo_pattern_set_extend(cairo_get_source(context), CAIRO_EXTEND_REPEAT); cairo_rectangle(context, rc.left, rc.top, rc.Width(), rc.Height()); cairo_fill(context); @@ -558,8 +556,8 @@ static void PathRoundRectangle(cairo_t *context, double left, double top, double void SurfaceImpl::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) { if (context && rc.Width() > 0) { - const float halfStroke = fillStroke.stroke.width / 2.0f; - const float doubleStroke = fillStroke.stroke.width * 2.0f; + const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0; + const XYPOSITION doubleStroke = fillStroke.stroke.width * 2.0; PenColourAlpha(fillStroke.fill.colour); if (cornerSize > 0) PathRoundRectangle(context, rc.left + fillStroke.stroke.width, rc.top + fillStroke.stroke.width, @@ -625,12 +623,10 @@ void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsi pixelsImage += RGBAImage::bytesPerPixel * width; } - cairo_surface_t *psurfImage = cairo_image_surface_create_for_data(&image[0], CAIRO_FORMAT_ARGB32, width, height, stride); - cairo_set_source_surface(context, psurfImage, rc.left, rc.top); + UniqueCairoSurface surfImage(cairo_image_surface_create_for_data(&image[0], CAIRO_FORMAT_ARGB32, width, height, stride)); + cairo_set_source_surface(context, surfImage.get(), rc.left, rc.top); cairo_rectangle(context, rc.left, rc.top, rc.Width(), rc.Height()); cairo_fill(context); - - cairo_surface_destroy(psurfImage); } void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) { @@ -704,10 +700,10 @@ void SurfaceImpl::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) { void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) { SurfaceImpl &surfi = static_cast(surfaceSource); - const bool canDraw = surfi.psurf != nullptr; + const bool canDraw = surfi.surf != nullptr; if (canDraw) { PLATFORM_ASSERT(context); - cairo_set_source_surface(context, surfi.psurf, + cairo_set_source_surface(context, surfi.surf.get(), rc.left - from.x, rc.top - from.y); cairo_rectangle(context, rc.left, rc.top, rc.Width(), rc.Height()); cairo_fill(context); @@ -778,21 +774,20 @@ void SurfaceImpl::DrawTextBase(PRectangle rc, const Font *font_, XYPOSITION ybas if (context) { PenColourAlpha(fore); const XYPOSITION xText = rc.left; - if (PFont(font_)->pfd) { - std::string utfForm; + if (PFont(font_)->fd) { if (et == EncodingType::utf8) { - pango_layout_set_text(layout, text.data(), text.length()); + LayoutSetText(layout.get(), text); } else { SetConverter(PFont(font_)->characterSet); - utfForm = UTF8FromIconv(conv, text); + std::string utfForm = UTF8FromIconv(conv, text); if (utfForm.empty()) { // iconv failed so treat as Latin1 utfForm = UTF8FromLatin1(text); } - pango_layout_set_text(layout, utfForm.c_str(), utfForm.length()); + LayoutSetText(layout.get(), utfForm); } - pango_layout_set_font_description(layout, PFont(font_)->pfd); - pango_cairo_update_layout(context, layout); - PangoLayoutLine *pll = pango_layout_get_line_readonly(layout, 0); + pango_layout_set_font_description(layout.get(), PFont(font_)->fd.get()); + pango_cairo_update_layout(context, layout.get()); + PangoLayoutLine *pll = pango_layout_get_line_readonly(layout.get(), 0); cairo_move_to(context, xText, ybase); pango_cairo_show_layout_line(context, pll); } @@ -823,54 +818,69 @@ void SurfaceImpl::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITI } } +namespace { + class ClusterIterator { - PangoLayoutIter *iter; - PangoRectangle pos; - size_t lenPositions; + UniquePangoLayoutIter iter; + PangoRectangle pos {}; + int lenPositions; public: - bool finished; - XYPOSITION positionStart; - XYPOSITION position; - XYPOSITION distance; - int curIndex; - ClusterIterator(PangoLayout *layout, size_t len) noexcept : lenPositions(len), finished(false), - positionStart(0), position(0), distance(0), curIndex(0) { - iter = pango_layout_get_iter(layout); - pango_layout_iter_get_cluster_extents(iter, nullptr, &pos); - } - // Deleted so ClusterIterator objects can not be copied. - ClusterIterator(const ClusterIterator&) = delete; - ClusterIterator(ClusterIterator&&) = delete; - ClusterIterator&operator=(const ClusterIterator&) = delete; - ClusterIterator&operator=(ClusterIterator&&) = delete; - - ~ClusterIterator() { - pango_layout_iter_free(iter); + bool finished = false; + XYPOSITION positionStart = 0.0; + XYPOSITION position = 0.0; + XYPOSITION distance = 0.0; + int curIndex = 0; + ClusterIterator(PangoLayout *layout, std::string_view text) noexcept : + lenPositions(static_cast(text.length())) { + LayoutSetText(layout, text); + iter.reset(pango_layout_get_iter(layout)); + curIndex = pango_layout_iter_get_index(iter.get()); + pango_layout_iter_get_cluster_extents(iter.get(), nullptr, &pos); } void Next() noexcept { positionStart = position; - if (pango_layout_iter_next_cluster(iter)) { - pango_layout_iter_get_cluster_extents(iter, nullptr, &pos); - position = floatFromPangoUnits(pos.x); - curIndex = pango_layout_iter_get_index(iter); + if (pango_layout_iter_next_cluster(iter.get())) { + pango_layout_iter_get_cluster_extents(iter.get(), nullptr, &pos); + position = pango_units_to_double(pos.x); + curIndex = pango_layout_iter_get_index(iter.get()); } else { finished = true; - position = floatFromPangoUnits(pos.x + pos.width); - curIndex = lenPositions; + position = pango_units_to_double(pos.x + pos.width); + curIndex = pango_layout_iter_get_index(iter.get()); } distance = position - positionStart; } }; +// Something has gone wrong so set all the characters as equally spaced. +void EquallySpaced(PangoLayout *layout, XYPOSITION *positions, size_t lenPositions) { + int widthLayout = 0; + pango_layout_get_size(layout, &widthLayout, nullptr); + const XYPOSITION widthTotal = pango_units_to_double(widthLayout); + for (size_t bytePos=0; bytePospfd) { - pango_layout_set_font_description(layout, PFont(font_)->pfd); + if (PFont(font_)->fd) { + UniquePangoContext contextMeasure = MeasuringContext(); + UniquePangoLayout layoutMeasure(pango_layout_new(contextMeasure.get())); + PLATFORM_ASSERT(layoutMeasure); + + pango_layout_set_font_description(layoutMeasure.get(), PFont(font_)->fd.get()); if (et == EncodingType::utf8) { // Simple and direct as UTF-8 is native Pango encoding - int i = 0; - pango_layout_set_text(layout, text.data(), text.length()); - ClusterIterator iti(layout, text.length()); + ClusterIterator iti(layoutMeasure.get(), text); + int i = iti.curIndex; + if (i != 0) { + // Unexpected start to iteration, could be bidirectional text + EquallySpaced(layoutMeasure.get(), positions, text.length()); + return; + } while (!iti.finished) { iti.Next(); const int places = iti.curIndex - i; @@ -886,22 +896,46 @@ void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSI PLATFORM_ASSERT(static_cast(i) == text.length()); } else { int positionsCalculated = 0; + const char *charSetID = CharacterSetID(PFont(font_)->characterSet); + std::string utfForm; + { + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError *error = nullptr; + UniqueStr textInUTF8(g_convert(text.data(), text.length(), + "UTF-8", charSetID, + &bytesRead, + &bytesWritten, + &error)); + if ((bytesWritten > 0) && (bytesRead == text.length()) && !error) { + // Extra allocation here but avoiding it makes code more complex + utfForm.assign(textInUTF8.get(), bytesWritten); + } + if (error) { +#ifdef DEBUG + fprintf(stderr, "MeasureWidths: %s.\n", error->message); +#endif + g_error_free(error); + } + } if (et == EncodingType::dbcs) { - SetConverter(PFont(font_)->characterSet); - std::string utfForm = UTF8FromIconv(conv, text); if (!utfForm.empty()) { // Convert to UTF-8 so can ask Pango for widths, then // Loop through UTF-8 and DBCS forms, taking account of different // character byte lengths. - Converter convMeasure("UCS-2", CharacterSetID(characterSet), false); - pango_layout_set_text(layout, utfForm.c_str(), strlen(utfForm.c_str())); + Converter convMeasure("UCS-2", charSetID, false); int i = 0; - int clusterStart = 0; - ClusterIterator iti(layout, strlen(utfForm.c_str())); + ClusterIterator iti(layoutMeasure.get(), utfForm); + int clusterStart = iti.curIndex; + if (clusterStart != 0) { + // Unexpected start to iteration, could be bidirectional text + EquallySpaced(layoutMeasure.get(), positions, text.length()); + return; + } while (!iti.finished) { iti.Next(); const int clusterEnd = iti.curIndex; - const int places = g_utf8_strlen(utfForm.c_str() + clusterStart, clusterEnd - clusterStart); + const int places = g_utf8_strlen(utfForm.data() + clusterStart, clusterEnd - clusterStart); int place = 1; while (clusterStart < clusterEnd) { size_t lenChar = MultiByteLenFromIconv(convMeasure, text.data()+i, text.length()-i); @@ -919,31 +953,35 @@ void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSI if (positionsCalculated < 1) { const size_t lenPositions = text.length(); // Either 8-bit or DBCS conversion failed so treat as 8-bit. - SetConverter(PFont(font_)->characterSet); const bool rtlCheck = PFont(font_)->characterSet == CharacterSet::Hebrew || PFont(font_)->characterSet == CharacterSet::Arabic; - std::string utfForm = UTF8FromIconv(conv, text); if (utfForm.empty()) { utfForm = UTF8FromLatin1(text); +#ifdef DEBUG + fprintf(stderr, "MeasureWidths: Fall back to Latin1 [%s]\n", utfForm.c_str()); +#endif } - pango_layout_set_text(layout, utfForm.c_str(), utfForm.length()); size_t i = 0; - int clusterStart = 0; // Each 8-bit input character may take 1 or 2 bytes in UTF-8 // and groups of up to 3 may be represented as ligatures. - ClusterIterator iti(layout, utfForm.length()); + ClusterIterator iti(layoutMeasure.get(), utfForm); + int clusterStart = iti.curIndex; + if (clusterStart != 0) { + // Unexpected start to iteration, could be bidirectional text + EquallySpaced(layoutMeasure.get(), positions, lenPositions); + return; + } while (!iti.finished) { iti.Next(); const int clusterEnd = iti.curIndex; - const int ligatureLength = g_utf8_strlen(utfForm.c_str() + clusterStart, clusterEnd - clusterStart); - if (rtlCheck && ((clusterEnd <= clusterStart) || (ligatureLength == 0) || (ligatureLength > 3))) { + const int ligatureLength = g_utf8_strlen(utfForm.data() + clusterStart, clusterEnd - clusterStart); + if (((i + ligatureLength) > lenPositions) || + (rtlCheck && ((clusterEnd <= clusterStart) || (ligatureLength == 0) || (ligatureLength > 3)))) { // Something has gone wrong: exit quickly but pretend all the characters are equally spaced: - int widthLayout = 0; - pango_layout_get_size(layout, &widthLayout, nullptr); - const XYPOSITION widthTotal = floatFromPangoUnits(widthLayout); - for (size_t bytePos=0; bytePos 0 && ligatureLength <= 3); @@ -962,29 +1000,28 @@ void SurfaceImpl::MeasureWidths(const Font *font_, std::string_view text, XYPOSI } else { // No font so return an ascending range of values for (size_t i = 0; i < text.length(); i++) { - positions[i] = i + 1; + positions[i] = i + 1.0; } } } XYPOSITION SurfaceImpl::WidthText(const Font *font_, std::string_view text) { - if (PFont(font_)->pfd) { - std::string utfForm; - pango_layout_set_font_description(layout, PFont(font_)->pfd); + if (PFont(font_)->fd) { + pango_layout_set_font_description(layout.get(), PFont(font_)->fd.get()); if (et == EncodingType::utf8) { - pango_layout_set_text(layout, text.data(), text.length()); + LayoutSetText(layout.get(), text); } else { SetConverter(PFont(font_)->characterSet); - utfForm = UTF8FromIconv(conv, text); + std::string utfForm = UTF8FromIconv(conv, text); if (utfForm.empty()) { // iconv failed so treat as Latin1 utfForm = UTF8FromLatin1(text); } - pango_layout_set_text(layout, utfForm.c_str(), utfForm.length()); + LayoutSetText(layout.get(), utfForm); } - PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout, 0); + PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout.get(), 0); PangoRectangle pos {}; pango_layout_line_get_extents(pangoLine, nullptr, &pos); - return floatFromPangoUnits(pos.width); + return pango_units_to_double(pos.width); } return 1; } @@ -994,11 +1031,11 @@ void SurfaceImpl::DrawTextBaseUTF8(PRectangle rc, const Font *font_, XYPOSITION if (context) { PenColourAlpha(fore); const XYPOSITION xText = rc.left; - if (PFont(font_)->pfd) { - pango_layout_set_text(layout, text.data(), text.length()); - pango_layout_set_font_description(layout, PFont(font_)->pfd); - pango_cairo_update_layout(context, layout); - PangoLayoutLine *pll = pango_layout_get_line_readonly(layout, 0); + if (PFont(font_)->fd) { + LayoutSetText(layout.get(), text); + pango_layout_set_font_description(layout.get(), PFont(font_)->fd.get()); + pango_cairo_update_layout(context, layout.get()); + PangoLayoutLine *pll = pango_layout_get_line_readonly(layout.get(), 0); cairo_move_to(context, xText, ybase); pango_cairo_show_layout_line(context, pll); } @@ -1030,12 +1067,20 @@ void SurfaceImpl::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPO } void SurfaceImpl::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) { - if (PFont(font_)->pfd) { - pango_layout_set_font_description(layout, PFont(font_)->pfd); + if (PFont(font_)->fd) { + UniquePangoContext contextMeasure = MeasuringContext(); + UniquePangoLayout layoutMeasure(pango_layout_new(contextMeasure.get())); + PLATFORM_ASSERT(layoutMeasure); + + pango_layout_set_font_description(layoutMeasure.get(), PFont(font_)->fd.get()); // Simple and direct as UTF-8 is native Pango encoding - int i = 0; - pango_layout_set_text(layout, text.data(), text.length()); - ClusterIterator iti(layout, text.length()); + ClusterIterator iti(layoutMeasure.get(), text); + int i = iti.curIndex; + if (i != 0) { + // Unexpected start to iteration, could be bidirectional text + EquallySpaced(layoutMeasure.get(), positions, text.length()); + return; + } while (!iti.finished) { iti.Next(); const int places = iti.curIndex - i; @@ -1052,19 +1097,19 @@ void SurfaceImpl::MeasureWidthsUTF8(const Font *font_, std::string_view text, XY } else { // No font so return an ascending range of values for (size_t i = 0; i < text.length(); i++) { - positions[i] = i + 1; + positions[i] = i + 1.0; } } } XYPOSITION SurfaceImpl::WidthTextUTF8(const Font *font_, std::string_view text) { - if (PFont(font_)->pfd) { - pango_layout_set_font_description(layout, PFont(font_)->pfd); - pango_layout_set_text(layout, text.data(), text.length()); - PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout, 0); + if (PFont(font_)->fd) { + pango_layout_set_font_description(layout.get(), PFont(font_)->fd.get()); + LayoutSetText(layout.get(), text); + PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout.get(), 0); PangoRectangle pos{}; pango_layout_line_get_extents(pangoLine, nullptr, &pos); - return floatFromPangoUnits(pos.width); + return pango_units_to_double(pos.width); } return 1; } @@ -1072,30 +1117,22 @@ XYPOSITION SurfaceImpl::WidthTextUTF8(const Font *font_, std::string_view text) // Ascent and descent determined by Pango font metrics. XYPOSITION SurfaceImpl::Ascent(const Font *font_) { - XYPOSITION ascent = 0; - if (PFont(font_)->pfd) { - PangoFontMetrics *metrics = pango_context_get_metrics(pcontext, - PFont(font_)->pfd, pango_context_get_language(pcontext)); - ascent = std::ceil(floatFromPangoUnits( - pango_font_metrics_get_ascent(metrics))); - pango_font_metrics_unref(metrics); + if (!PFont(font_)->fd) { + return 1.0; } - if (ascent == 0) { - ascent = 1; - } - return ascent; + UniquePangoFontMetrics metrics(pango_context_get_metrics(pcontext.get(), + PFont(font_)->fd.get(), language)); + return std::max(1.0, std::ceil(pango_units_to_double( + pango_font_metrics_get_ascent(metrics.get())))); } XYPOSITION SurfaceImpl::Descent(const Font *font_) { - if (PFont(font_)->pfd) { - PangoFontMetrics *metrics = pango_context_get_metrics(pcontext, - PFont(font_)->pfd, pango_context_get_language(pcontext)); - const XYPOSITION descent = std::ceil(floatFromPangoUnits( - pango_font_metrics_get_descent(metrics))); - pango_font_metrics_unref(metrics); - return descent; + if (!PFont(font_)->fd) { + return 0.0; } - return 0; + UniquePangoFontMetrics metrics(pango_context_get_metrics(pcontext.get(), + PFont(font_)->fd.get(), language)); + return std::ceil(pango_units_to_double(pango_font_metrics_get_descent(metrics.get()))); } XYPOSITION SurfaceImpl::InternalLeading(const Font *) { @@ -1292,11 +1329,7 @@ void Window::SetCursor(Cursor curs) { if (WindowFromWidget(PWidget(wid))) gdk_window_set_cursor(WindowFromWidget(PWidget(wid)), gdkCurs); -#if GTK_CHECK_VERSION(3,0,0) - g_object_unref(gdkCurs); -#else - gdk_cursor_unref(gdkCurs); -#endif + UnRefCursor(gdkCurs); } /* Returns rectangle of monitor pt is on, both rect and pt are in Window's @@ -1316,7 +1349,7 @@ PRectangle Window::GetMonitorRect(Point pt) { #else GdkScreen *screen = gtk_widget_get_screen(PWidget(wid)); const gint monitor_num = gdk_screen_get_monitor_at_point(screen, - pt.x + x_offset, pt.y + y_offset); + static_cast(pt.x) + x_offset, static_cast(pt.y) + y_offset); gdk_screen_get_monitor_geometry(screen, monitor_num, &rect); #endif rect.x -= x_offset; @@ -1355,7 +1388,7 @@ class ListBoxX : public ListBox { WindowID frame; WindowID list; WindowID scroller; - void *pixhash; + GHashTable *pixhash; GtkCellRenderer *pixbuf_renderer; GtkCellRenderer *renderer; RGBAImageSet images; @@ -1363,7 +1396,7 @@ class ListBoxX : public ListBox { unsigned int maxItemCharacters; unsigned int aveCharWidth; #if GTK_CHECK_VERSION(3,0,0) - GtkCssProvider *cssProvider; + std::unique_ptr cssProvider; #endif public: IListBoxDelegate *delegate; @@ -1373,9 +1406,6 @@ class ListBoxX : public ListBox { renderer(nullptr), desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(1), -#if GTK_CHECK_VERSION(3,0,0) - cssProvider(nullptr), -#endif delegate(nullptr) { } // Deleted so ListBoxX objects can not be copied. @@ -1385,19 +1415,13 @@ class ListBoxX : public ListBox { ListBoxX&operator=(ListBoxX&&) = delete; ~ListBoxX() noexcept override { if (pixhash) { - g_hash_table_foreach((GHashTable *) pixhash, list_image_free, nullptr); - g_hash_table_destroy((GHashTable *) pixhash); + g_hash_table_foreach(pixhash, list_image_free, nullptr); + g_hash_table_destroy(pixhash); } if (widCached) { gtk_widget_destroy(GTK_WIDGET(widCached)); wid = widCached = nullptr; } -#if GTK_CHECK_VERSION(3,0,0) - if (cssProvider) { - g_object_unref(cssProvider); - cssProvider = nullptr; - } -#endif } void SetFont(const Font *font) override; void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_, Technology technology_) override; @@ -1506,7 +1530,7 @@ static void small_scroller_class_init(SmallScrollerClass *klass) { static void small_scroller_init(SmallScroller *) {} -static gboolean ButtonPress(GtkWidget *, GdkEventButton *ev, gpointer p) { +static gboolean ButtonPress(GtkWidget *, const GdkEventButton *ev, gpointer p) { try { ListBoxX *lb = static_cast(p); if (ev->type == GDK_2BUTTON_PRESS && lb->delegate) { @@ -1521,7 +1545,7 @@ static gboolean ButtonPress(GtkWidget *, GdkEventButton *ev, gpointer p) { return FALSE; } -static gboolean ButtonRelease(GtkWidget *, GdkEventButton *ev, gpointer p) { +static gboolean ButtonRelease(GtkWidget *, const GdkEventButton *ev, gpointer p) { try { ListBoxX *lb = static_cast(p); if (ev->type != GDK_2BUTTON_PRESS && lb->delegate) { @@ -1593,7 +1617,7 @@ void ListBoxX::Create(Window &parent, int, Point, int, bool, Technology) { #if GTK_CHECK_VERSION(3,0,0) if (!cssProvider) { - cssProvider = gtk_css_provider_new(); + cssProvider.reset(gtk_css_provider_new()); } #endif @@ -1622,7 +1646,7 @@ void ListBoxX::Create(Window &parent, int, Point, int, bool, Technology) { #if GTK_CHECK_VERSION(3,0,0) GtkStyleContext *styleContext = gtk_widget_get_style_context(GTK_WIDGET(list)); if (styleContext) { - gtk_style_context_add_provider(styleContext, GTK_STYLE_PROVIDER(cssProvider), + gtk_style_context_add_provider(styleContext, GTK_STYLE_PROVIDER(cssProvider.get()), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } #endif @@ -1669,11 +1693,11 @@ void ListBoxX::Create(Window &parent, int, Point, int, bool, Technology) { void ListBoxX::SetFont(const Font *font) { // Only do for Pango font as there have been crashes for GDK fonts - if (Created() && PFont(font)->pfd) { + if (Created() && PFont(font)->fd) { // Current font is Pango font #if GTK_CHECK_VERSION(3,0,0) if (cssProvider) { - PangoFontDescription *pfd = PFont(font)->pfd; + PangoFontDescription *pfd = PFont(font)->fd.get(); std::ostringstream ssFontSetting; ssFontSetting << "GtkTreeView, treeview { "; ssFontSetting << "font-family: " << pango_font_description_get_family(pfd) << "; "; @@ -1690,11 +1714,11 @@ void ListBoxX::SetFont(const Font *font) { } ssFontSetting << "font-weight:"<< pango_font_description_get_weight(pfd) << "; "; ssFontSetting << "}"; - gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(cssProvider), + gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(cssProvider.get()), ssFontSetting.str().c_str(), -1, nullptr); } #else - gtk_widget_modify_font(PWidget(list), PFont(font)->pfd); + gtk_widget_modify_font(PWidget(list), PFont(font)->fd.get()); #endif gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), -1); gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1); @@ -1778,9 +1802,7 @@ PRectangle ListBoxX::GetDesiredRect() { #endif rc.bottom = height; - int width = maxItemCharacters; - if (width < 12) - width = 12; + const unsigned int width = std::max(maxItemCharacters, 12U); rc.right = width * (aveCharWidth + aveCharWidth / 3); // Add horizontal padding and borders int horizontal_separator=0; @@ -1847,7 +1869,7 @@ static void init_pixmap(ListImage *list_image) noexcept { void ListBoxX::Append(char *s, int type) { ListImage *list_image = nullptr; if ((type >= 0) && pixhash) { - list_image = static_cast(g_hash_table_lookup((GHashTable *) pixhash, + list_image = static_cast(g_hash_table_lookup(pixhash, GINT_TO_POINTER(type))); } GtkTreeIter iter {}; @@ -1877,7 +1899,7 @@ void ListBoxX::Append(char *s, int type) { gtk_list_store_set(GTK_LIST_STORE(store), &iter, TEXT_COLUMN, s, -1); } - const size_t len = strlen(s); + const unsigned int len = static_cast(strlen(s)); if (maxItemCharacters < len) maxItemCharacters = len; } @@ -1913,7 +1935,7 @@ void ListBoxX::Select(int n) { GtkAdjustment *adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list)); #endif - gfloat value = (static_cast(n) / total) * (gtk_adjustment_get_upper(adj) - gtk_adjustment_get_lower(adj)) + gdouble value = (static_cast(n) / total) * (gtk_adjustment_get_upper(adj) - gtk_adjustment_get_lower(adj)) + gtk_adjustment_get_lower(adj) - gtk_adjustment_get_page_size(adj) / 2; // Get cell height const int row_height = GetRowHeight(); @@ -2008,7 +2030,7 @@ void ListBoxX::RegisterRGBA(int type, std::unique_ptr image) { if (!pixhash) { pixhash = g_hash_table_new(g_direct_hash, g_direct_equal); } - ListImage *list_image = static_cast(g_hash_table_lookup((GHashTable *) pixhash, + ListImage *list_image = static_cast(g_hash_table_lookup(pixhash, GINT_TO_POINTER(type))); if (list_image) { // Drop icon already registered @@ -2019,7 +2041,7 @@ void ListBoxX::RegisterRGBA(int type, std::unique_ptr image) { } else { list_image = g_new0(ListImage, 1); list_image->rgba_data = observe; - g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type), + g_hash_table_insert(pixhash, GINT_TO_POINTER(type), (gpointer) list_image); } } @@ -2087,7 +2109,7 @@ void Menu::Destroy() noexcept { #if !GTK_CHECK_VERSION(3,22,0) static void MenuPositionFunc(GtkMenu *, gint *x, gint *y, gboolean *, gpointer userData) noexcept { - sptr_t intFromPointer = GPOINTER_TO_INT(userData); + const gint intFromPointer = GPOINTER_TO_INT(userData); *x = intFromPointer & 0xffff; *y = intFromPointer >> 16; } diff --git a/scintilla/gtk/ScintillaGTK.cxx b/scintilla/gtk/ScintillaGTK.cxx index 7a8c4d0b5e..4912d5998a 100644 --- a/scintilla/gtk/ScintillaGTK.cxx +++ b/scintilla/gtk/ScintillaGTK.cxx @@ -80,10 +80,10 @@ #include "AutoComplete.h" #include "ScintillaBase.h" +#include "Wrappers.h" #include "ScintillaGTK.h" #include "scintilla-marshal.h" #include "ScintillaGTKAccessible.h" - #include "Converter.h" #define IS_WIDGET_REALIZED(w) (gtk_widget_get_realized(GTK_WIDGET(w))) @@ -135,10 +135,6 @@ constexpr gint nClipboardPasteTargets = static_cast(std::size(clipboardPas const GdkDragAction actionCopyOrMove = static_cast(GDK_ACTION_COPY | GDK_ACTION_MOVE); -GdkWindow *WindowFromWidget(GtkWidget *w) noexcept { - return gtk_widget_get_window(w); -} - GtkWidget *PWidget(const Window &w) noexcept { return static_cast(w.GetID()); } @@ -148,14 +144,6 @@ GdkWindow *PWindow(const Window &w) noexcept { return gtk_widget_get_window(widget); } -void UnRefCursor(GdkCursor *cursor) noexcept { -#if GTK_CHECK_VERSION(3,0,0) - g_object_unref(cursor); -#else - gdk_cursor_unref(cursor); -#endif -} - void MapWidget(GtkWidget *widget) noexcept { if (widget && gtk_widget_get_visible(GTK_WIDGET(widget)) && @@ -195,9 +183,9 @@ bool SettingGet(GtkSettings *settings, const gchar *name, gpointer value) noexce } FontOptions::FontOptions(GtkWidget *widget) noexcept { - PangoContext *pcontext = gtk_widget_create_pango_context(widget); + UniquePangoContext pcontext(gtk_widget_create_pango_context(widget)); PLATFORM_ASSERT(pcontext); - const cairo_font_options_t *options = pango_cairo_context_get_font_options(pcontext); + const cairo_font_options_t *options = pango_cairo_context_get_font_options(pcontext.get()); // options is owned by the PangoContext so must not be freed. if (options) { // options is NULL on Win32 @@ -205,7 +193,6 @@ FontOptions::FontOptions(GtkWidget *widget) noexcept { order = cairo_font_options_get_subpixel_order(options); hint = cairo_font_options_get_hint_style(options); } - g_object_unref(pcontext); } bool FontOptions::operator==(const FontOptions &other) const noexcept { @@ -222,7 +209,6 @@ ScintillaGTK *ScintillaGTK::FromWidget(GtkWidget *widget) noexcept { ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) : adjustmentv(nullptr), adjustmenth(nullptr), verticalScrollBarWidth(30), horizontalScrollBarHeight(30), - evbtn(nullptr), buttonMouse(0), capturedMouse(false), dragWasDropped(false), lastKey(0), rectangularSelectionModifier(SCMOD_CTRL), @@ -273,9 +259,9 @@ ScintillaGTK::~ScintillaGTK() { g_source_remove(styleIdleID); styleIdleID = 0; } - if (evbtn) { - gdk_event_free(evbtn); - evbtn = nullptr; + if (scrollBarIdleID) { + g_source_remove(scrollBarIdleID); + scrollBarIdleID = 0; } ClearPrimarySelection(); wPreedit.Destroy(); @@ -317,7 +303,6 @@ void ScintillaGTK::RealizeThis(GtkWidget *widget) { gtk_widget_get_window(widget)); #endif gdk_window_show(gtk_widget_get_window(widget)); - UnRefCursor(cursor); #else widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attrs, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR); @@ -325,19 +310,19 @@ void ScintillaGTK::RealizeThis(GtkWidget *widget) { widget->style = gtk_style_attach(widget->style, widget->window); gdk_window_set_background(widget->window, &widget->style->bg[GTK_STATE_NORMAL]); gdk_window_show(widget->window); - UnRefCursor(cursor); #endif + UnRefCursor(cursor); preeditInitialized = false; gtk_widget_realize(PWidget(wPreedit)); gtk_widget_realize(PWidget(wPreeditDraw)); - im_context = gtk_im_multicontext_new(); - g_signal_connect(G_OBJECT(im_context), "commit", + im_context.reset(gtk_im_multicontext_new()); + g_signal_connect(G_OBJECT(im_context.get()), "commit", G_CALLBACK(Commit), this); - g_signal_connect(G_OBJECT(im_context), "preedit_changed", + g_signal_connect(G_OBJECT(im_context.get()), "preedit_changed", G_CALLBACK(PreeditChanged), this); - gtk_im_context_set_client_window(im_context, WindowFromWidget(widget)); + gtk_im_context_set_client_window(im_context.get(), WindowFromWidget(widget)); GtkWidget *widtxt = PWidget(wText); // // No code inside the G_OBJECT macro g_signal_connect_after(G_OBJECT(widtxt), "style_set", @@ -388,8 +373,7 @@ void ScintillaGTK::UnRealizeThis(GtkWidget *widget) { gtk_widget_unrealize(PWidget(scrollbarh)); gtk_widget_unrealize(PWidget(wPreedit)); gtk_widget_unrealize(PWidget(wPreeditDraw)); - g_object_unref(im_context); - im_context = nullptr; + im_context.reset(); if (GTK_WIDGET_CLASS(parentClass)->unrealize) GTK_WIDGET_CLASS(parentClass)->unrealize(widget); @@ -504,8 +488,8 @@ gint ScintillaGTK::FocusInThis(GtkWidget *) { SetFocusState(true); if (im_context) { - gtk_im_context_focus_in(im_context); - PreEditString pes(im_context); + gtk_im_context_focus_in(im_context.get()); + PreEditString pes(im_context.get()); if (PWidget(wPreedit)) { if (!preeditInitialized) { GtkWidget *top = gtk_widget_get_toplevel(PWidget(wMain)); @@ -538,7 +522,7 @@ gint ScintillaGTK::FocusOutThis(GtkWidget *) { if (PWidget(wPreedit)) gtk_widget_hide(PWidget(wPreedit)); if (im_context) - gtk_im_context_focus_out(im_context); + gtk_im_context_focus_out(im_context.get()); } catch (...) { errorStatus = Status::Failure; @@ -765,14 +749,14 @@ void ScintillaGTK::StartDrag() { tl, actionCopyOrMove, buttonMouse, - evbtn, + evbtn.get(), -1, -1); #else gtk_drag_begin(GTK_WIDGET(PWidget(wMain)), tl, actionCopyOrMove, buttonMouse, - evbtn); + evbtn.get()); #endif } @@ -1129,6 +1113,7 @@ bool ScintillaGTK::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) { #if !GTK_CHECK_VERSION(3,18,0) gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv)); #endif + gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), static_cast(topLine)); modified = true; } @@ -1150,6 +1135,7 @@ bool ScintillaGTK::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) { #if !GTK_CHECK_VERSION(3,18,0) gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth)); #endif + gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset); modified = true; } if (modified && (paintState == PaintState::painting)) { @@ -1164,6 +1150,27 @@ void ScintillaGTK::ReconfigureScrollBars() { Resize(static_cast(rc.Width()), static_cast(rc.Height())); } +void ScintillaGTK::SetScrollBars() { + if (scrollBarIdleID) { + // Only allow one scroll bar change to be queued + return; + } + constexpr gint priorityScrollBar = GDK_PRIORITY_REDRAW + 5; + // On GTK, unlike other platforms, modifying scrollbars inside some events including + // resizes causes problems. Deferring the modification to a lower priority (125) idle + // event avoids the problems. This code did not always work when the priority was + // higher than GTK's resize (GTK_PRIORITY_RESIZE=110) or redraw + // (GDK_PRIORITY_REDRAW=120) idle tasks. + scrollBarIdleID = gdk_threads_add_idle_full(priorityScrollBar, + [](gpointer pSci) -> gboolean { + ScintillaGTK *sciThis = static_cast(pSci); + sciThis->ChangeScrollBars(); + sciThis->scrollBarIdleID = 0; + return FALSE; + }, + this, nullptr); +} + void ScintillaGTK::NotifyChange() { g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0, Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE), PWidget(wMain)); @@ -1207,11 +1214,12 @@ const char *ScintillaGTK::CharacterSetID() const { return ::CharacterSetID(vs.styles[STYLE_DEFAULT].characterSet); } +namespace { + class CaseFolderDBCS : public CaseFolderTable { const char *charSet; public: explicit CaseFolderDBCS(const char *charSet_) noexcept : charSet(charSet_) { - StandardASCII(); } size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override { if ((lenMixed == 1) && (sizeFolded > 0)) { @@ -1221,15 +1229,14 @@ class CaseFolderDBCS : public CaseFolderTable { std::string sUTF8 = ConvertText(mixed, lenMixed, "UTF-8", charSet, false); if (!sUTF8.empty()) { - gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length()); - size_t lenMapped = strlen(mapped); + UniqueStr mapped(g_utf8_casefold(sUTF8.c_str(), sUTF8.length())); + size_t lenMapped = strlen(mapped.get()); if (lenMapped < sizeFolded) { - memcpy(folded, mapped, lenMapped); + memcpy(folded, mapped.get(), lenMapped); } else { folded[0] = '\0'; lenMapped = 1; } - g_free(mapped); return lenMapped; } } @@ -1239,6 +1246,8 @@ class CaseFolderDBCS : public CaseFolderTable { } }; +} + std::unique_ptr ScintillaGTK::CaseFolderForEncoding() { if (pdoc->dbcsCodePage == SC_CP_UTF8) { return std::make_unique(); @@ -1247,7 +1256,6 @@ std::unique_ptr ScintillaGTK::CaseFolderForEncoding() { if (charSetBuffer) { if (pdoc->dbcsCodePage == 0) { std::unique_ptr pcf = std::make_unique(); - pcf->StandardASCII(); // Only for single byte encodings for (int i=0x80; i<0x100; i++) { char sCharacter[2] = "A"; @@ -1256,14 +1264,13 @@ std::unique_ptr ScintillaGTK::CaseFolderForEncoding() { std::string sUTF8 = ConvertText(sCharacter, 1, "UTF-8", charSetBuffer, false, true); if (!sUTF8.empty()) { - gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length()); + UniqueStr mapped(g_utf8_casefold(sUTF8.c_str(), sUTF8.length())); if (mapped) { - std::string mappedBack = ConvertText(mapped, strlen(mapped), + std::string mappedBack = ConvertText(mapped.get(), strlen(mapped.get()), charSetBuffer, "UTF-8", false, true); if ((mappedBack.length() == 1) && (mappedBack[0] != sCharacter[0])) { pcf->SetTranslation(sCharacter[0], mappedBack[0]); } - g_free(mapped); } } } @@ -1279,22 +1286,14 @@ std::unique_ptr ScintillaGTK::CaseFolderForEncoding() { namespace { struct CaseMapper { - gchar *mapped; // Must be freed with g_free + UniqueStr mapped; CaseMapper(const std::string &sUTF8, bool toUpperCase) noexcept { if (toUpperCase) { - mapped = g_utf8_strup(sUTF8.c_str(), sUTF8.length()); + mapped.reset(g_utf8_strup(sUTF8.c_str(), sUTF8.length())); } else { - mapped = g_utf8_strdown(sUTF8.c_str(), sUTF8.length()); + mapped.reset(g_utf8_strdown(sUTF8.c_str(), sUTF8.length())); } } - // Deleted so CaseMapper objects can not be copied. - CaseMapper(const CaseMapper&) = delete; - CaseMapper(CaseMapper&&) = delete; - CaseMapper&operator=(const CaseMapper&) = delete; - CaseMapper&operator=(CaseMapper&&) = delete; - ~CaseMapper() noexcept { - g_free(mapped); - } }; } @@ -1315,13 +1314,13 @@ std::string ScintillaGTK::CaseMapString(const std::string &s, CaseMapping caseMa if (!*charSetBuffer) { CaseMapper mapper(s, caseMapping == CaseMapping::upper); - return std::string(mapper.mapped, strlen(mapper.mapped)); + return std::string(mapper.mapped.get()); } else { // Change text to UTF-8 std::string sUTF8 = ConvertText(s.c_str(), s.length(), "UTF-8", charSetBuffer, false); CaseMapper mapper(sUTF8, caseMapping == CaseMapping::upper); - return ConvertText(mapper.mapped, strlen(mapper.mapped), charSetBuffer, "UTF-8", false); + return ConvertText(mapper.mapped.get(), strlen(mapper.mapped.get()), charSetBuffer, "UTF-8", false); } } @@ -1849,10 +1848,7 @@ gint ScintillaGTK::PressThis(GdkEventButton *event) { if (event->type != GDK_BUTTON_PRESS) return FALSE; - if (evbtn) { - gdk_event_free(evbtn); - } - evbtn = gdk_event_copy(reinterpret_cast(event)); + evbtn.reset(gdk_event_copy(reinterpret_cast(event))); buttonMouse = event->button; const Point pt = PointOfEvent(event); const PRectangle rcClient = GetClientRectangle(); @@ -1994,7 +1990,7 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) { // Compute amount and direction to scroll (even tho on win32 there is // intensity of scrolling info in the native message, gtk doesn't // support this so we simulate similarly adaptive scrolling) - // Note that this is disabled on OS X (Darwin) with the X11 backend + // Note that this is disabled on macOS (Darwin) with the X11 backend // where the X11 server already has an adaptive scrolling algorithm // that fights with this one int cLineScroll; @@ -2028,11 +2024,6 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) { // issues spurious button 2 mouse events during wheeling, which can cause // problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001) - // Data zoom not supported - if (event->state & GDK_SHIFT_MASK) { - return FALSE; - } - #if GTK_CHECK_VERSION(3,4,0) // Smooth scrolling not supported if (event->direction == GDK_SCROLL_SMOOTH) { @@ -2041,8 +2032,10 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) { #endif // Horizontal scrolling - if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT) { - sciThis->HorizontalScrollTo(sciThis->xOffset + cLineScroll); + if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT || event->state & GDK_SHIFT_MASK) { + int hScroll = gtk_adjustment_get_step_increment(sciThis->adjustmenth); + hScroll *= cLineScroll; // scroll by this many characters + sciThis->HorizontalScrollTo(sciThis->xOffset + hScroll); // Text font size zoom } else if (event->state & GDK_CONTROL_MASK) { @@ -2249,7 +2242,7 @@ gboolean ScintillaGTK::KeyThis(GdkEventKey *event) { try { //fprintf(stderr, "SC-key: %d %x [%s]\n", // event->keyval, event->state, (event->length > 0) ? event->string : "empty"); - if (gtk_im_context_filter_keypress(im_context, event)) { + if (gtk_im_context_filter_keypress(im_context.get(), event)) { return 1; } if (!event->keyval) { @@ -2307,7 +2300,7 @@ gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) { gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) { //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string); ScintillaGTK *sciThis = FromWidget(widget); - if (gtk_im_context_filter_keypress(sciThis->im_context, event)) { + if (gtk_im_context_filter_keypress(sciThis->im_context.get(), event)) { return TRUE; } return FALSE; @@ -2317,14 +2310,12 @@ gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) { gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *, cairo_t *cr) { try { - PreEditString pes(im_context); - PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); - pango_layout_set_attributes(layout, pes.attrs); + PreEditString pes(im_context.get()); + UniquePangoLayout layout(gtk_widget_create_pango_layout(PWidget(wText), pes.str)); + pango_layout_set_attributes(layout.get(), pes.attrs); cairo_move_to(cr, 0, 0); - pango_cairo_show_layout(cr, layout); - - g_object_unref(layout); + pango_cairo_show_layout(cr, layout.get()); } catch (...) { errorStatus = Status::Failure; } @@ -2339,15 +2330,13 @@ gboolean ScintillaGTK::DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *) { try { - PreEditString pes(im_context); - PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); - pango_layout_set_attributes(layout, pes.attrs); - - cairo_t *context = gdk_cairo_create(WindowFromWidget(widget)); - cairo_move_to(context, 0, 0); - pango_cairo_show_layout(context, layout); - cairo_destroy(context); - g_object_unref(layout); + PreEditString pes(im_context.get()); + UniquePangoLayout layout(gtk_widget_create_pango_layout(PWidget(wText), pes.str)); + pango_layout_set_attributes(layout.get(), pes.attrs); + + UniqueCairo context(gdk_cairo_create(WindowFromWidget(widget))); + cairo_move_to(context.get(), 0, 0); + pango_cairo_show_layout(context.get(), layout.get()); } catch (...) { errorStatus = Status::Failure; } @@ -2361,7 +2350,7 @@ gboolean ScintillaGTK::ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, Sci #endif bool ScintillaGTK::KoreanIME() { - PreEditString pes(im_context); + PreEditString pes(im_context.get()); if (pes.pscript != G_UNICODE_SCRIPT_COMMON) lastNonCommonScript = pes.pscript; return lastNonCommonScript == G_UNICODE_SCRIPT_HANGUL; @@ -2456,7 +2445,7 @@ void ScintillaGTK::SetCandidateWindowPos() { imeBox.y = static_cast(pt.y + std::max(4, vs.lineHeight/4)); // prevent overlapping with current line imeBox.height = vs.lineHeight; - gtk_im_context_set_cursor_location(im_context, &imeBox); + gtk_im_context_set_cursor_location(im_context.get(), &imeBox); } void ScintillaGTK::CommitThis(char *commitStr) { @@ -2497,7 +2486,7 @@ void ScintillaGTK::PreeditChangedInlineThis() { // Great thanks for my foreruners, jiniya and BLUEnLIVE try { if (pdoc->IsReadOnly() || SelectionContainsProtected()) { - gtk_im_context_reset(im_context); + gtk_im_context_reset(im_context.get()); return; } @@ -2512,7 +2501,7 @@ void ScintillaGTK::PreeditChangedInlineThis() { initialCompose = true; } - PreEditString preeditStr(im_context); + PreEditString preeditStr(im_context.get()); const char *charSetSource = CharacterSetID(); if (!preeditStr.validUTF8 || (charSetSource == nullptr)) { @@ -2571,16 +2560,15 @@ void ScintillaGTK::PreeditChangedInlineThis() { void ScintillaGTK::PreeditChangedWindowedThis() { try { - PreEditString pes(im_context); + PreEditString pes(im_context.get()); if (strlen(pes.str) > 0) { SetCandidateWindowPos(); - PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str); - pango_layout_set_attributes(layout, pes.attrs); + UniquePangoLayout layout(gtk_widget_create_pango_layout(PWidget(wText), pes.str)); + pango_layout_set_attributes(layout.get(), pes.attrs); gint w, h; - pango_layout_get_pixel_size(layout, &w, &h); - g_object_unref(layout); + pango_layout_get_pixel_size(layout.get(), &w, &h); gint x, y; gdk_window_get_origin(PWindow(wText), &x, &y); @@ -2690,13 +2678,13 @@ gboolean ScintillaGTK::DrawTextThis(cairo_t *cr) { rcPaint = GetClientRectangle(); - PLATFORM_ASSERT(rgnUpdate == nullptr); + cairo_rectangle_list_t *oldRgnUpdate = rgnUpdate; rgnUpdate = cairo_copy_clip_rectangle_list(cr); if (rgnUpdate && rgnUpdate->status != CAIRO_STATUS_SUCCESS) { // If not successful then ignore fprintf(stderr, "DrawTextThis failed to copy update region %d [%d]\n", rgnUpdate->status, rgnUpdate->num_rectangles); cairo_rectangle_list_destroy(rgnUpdate); - rgnUpdate = 0; + rgnUpdate = nullptr; } double x1, y1, x2, y2; @@ -2721,7 +2709,7 @@ gboolean ScintillaGTK::DrawTextThis(cairo_t *cr) { if (rgnUpdate) { cairo_rectangle_list_destroy(rgnUpdate); } - rgnUpdate = 0; + rgnUpdate = oldRgnUpdate; paintState = PaintState::notPainting; } catch (...) { errorStatus = Status::Failure; @@ -2793,16 +2781,16 @@ gboolean ScintillaGTK::ExposeTextThis(GtkWidget * /*widget*/, GdkEventExpose *os ose->area.x + ose->area.width, ose->area.y + ose->area.height); - PLATFORM_ASSERT(rgnUpdate == nullptr); + GdkRegion *oldRgnUpdate = rgnUpdate; rgnUpdate = gdk_region_copy(ose->region); const PRectangle rcClient = GetClientRectangle(); paintingAllText = rcPaint.Contains(rcClient); - std::unique_ptr surfaceWindow(Surface::Allocate(Technology::Default)); - cairo_t *cr = gdk_cairo_create(PWindow(wText)); - surfaceWindow->Init(cr, PWidget(wText)); - Paint(surfaceWindow.get(), rcPaint); - surfaceWindow->Release(); - cairo_destroy(cr); + { + std::unique_ptr surfaceWindow(Surface::Allocate(Technology::Default)); + UniqueCairo cr(gdk_cairo_create(PWindow(wText))); + surfaceWindow->Init(cr.get(), PWidget(wText)); + Paint(surfaceWindow.get(), rcPaint); + } if ((paintState == PaintState::abandoned) || repaintFullWindow) { // Painting area was insufficient to cover new styling or brace highlight positions FullPaint(); @@ -2813,7 +2801,7 @@ gboolean ScintillaGTK::ExposeTextThis(GtkWidget * /*widget*/, GdkEventExpose *os if (rgnUpdate) { gdk_region_destroy(rgnUpdate); } - rgnUpdate = nullptr; + rgnUpdate = oldRgnUpdate; } catch (...) { errorStatus = Status::Failure; } @@ -3105,12 +3093,10 @@ gboolean ScintillaGTK::DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip) { gboolean ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, CallTip *ctip) { try { std::unique_ptr surfaceWindow(Surface::Allocate(Technology::Default)); - cairo_t *cr = gdk_cairo_create(WindowFromWidget(widget)); - surfaceWindow->Init(cr, widget); + UniqueCairo cr(gdk_cairo_create(WindowFromWidget(widget))); + surfaceWindow->Init(cr.get(), widget); surfaceWindow->SetMode(SurfaceMode(ctip->codePage, false)); ctip->PaintCT(surfaceWindow.get()); - surfaceWindow->Release(); - cairo_destroy(cr); } catch (...) { // No pointer back to Scintilla to save status } @@ -3249,9 +3235,9 @@ void ScintillaGTK::ClassInit(OBJECT_CLASS *object_class, GtkWidgetClass *widget_ static void scintilla_class_init(ScintillaClass *klass) { try { - OBJECT_CLASS *object_class = (OBJECT_CLASS *) klass; - GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; - GtkContainerClass *container_class = (GtkContainerClass *) klass; + OBJECT_CLASS *object_class = reinterpret_cast(klass); + GtkWidgetClass *widget_class = reinterpret_cast(klass); + GtkContainerClass *container_class = reinterpret_cast(klass); const GSignalFlags sigflags = static_cast(G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST); scintilla_signals[COMMAND_SIGNAL] = g_signal_new( diff --git a/scintilla/gtk/ScintillaGTK.h b/scintilla/gtk/ScintillaGTK.h index 36e6a78a89..5db4ed3a5d 100644 --- a/scintilla/gtk/ScintillaGTK.h +++ b/scintilla/gtk/ScintillaGTK.h @@ -36,7 +36,7 @@ class ScintillaGTK : public ScintillaBase { SelectionText primary; SelectionPosition posPrimary; - GdkEvent *evbtn; + UniqueGdkEvent evbtn; guint buttonMouse; bool capturedMouse; bool dragWasDropped; @@ -60,7 +60,7 @@ class ScintillaGTK : public ScintillaBase { bool preeditInitialized; Window wPreedit; Window wPreeditDraw; - GtkIMContext *im_context; + UniqueIMContext im_context; GUnicodeScript lastNonCommonScript; GtkSettings *settings; @@ -82,6 +82,7 @@ class ScintillaGTK : public ScintillaBase { bool repaintFullWindow; guint styleIdleID; + guint scrollBarIdleID = 0; FontOptions fontOptionsPrevious; int accessibilityEnabled; AtkObject *accessible; @@ -133,6 +134,7 @@ class ScintillaGTK : public ScintillaBase { void SetHorizontalScrollPos() override; bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) override; void ReconfigureScrollBars() override; + void SetScrollBars() override; void NotifyChange() override; void NotifyFocus(bool focus) override; void NotifyParent(Scintilla::NotificationData scn) override; diff --git a/scintilla/gtk/ScintillaGTKAccessible.cxx b/scintilla/gtk/ScintillaGTKAccessible.cxx index e3c50e5105..ae6b0fb353 100644 --- a/scintilla/gtk/ScintillaGTKAccessible.cxx +++ b/scintilla/gtk/ScintillaGTKAccessible.cxx @@ -131,6 +131,7 @@ #include "AutoComplete.h" #include "ScintillaBase.h" +#include "Wrappers.h" #include "ScintillaGTK.h" #include "ScintillaGTKAccessible.h" @@ -187,7 +188,7 @@ gchar *ScintillaGTKAccessible::GetTextRangeUTF8(Sci::Position startByte, Sci::Po // like TargetAsUTF8, but avoids a double conversion if (sci->IsUnicodeMode() || ! *(charSetBuffer = sci->CharacterSetID())) { int len = endByte - startByte; - utf8Text = (char *) g_malloc(len + 1); + utf8Text = static_cast(g_malloc(len + 1)); sci->pdoc->GetCharRange(utf8Text, startByte, len); utf8Text[len] = '\0'; } else { @@ -195,7 +196,7 @@ gchar *ScintillaGTKAccessible::GetTextRangeUTF8(Sci::Position startByte, Sci::Po std::string s = sci->RangeText(startByte, endByte); std::string tmputf = ConvertText(&s[0], s.length(), "UTF-8", charSetBuffer, false); size_t len = tmputf.length(); - utf8Text = (char *) g_malloc(len + 1); + utf8Text = static_cast(g_malloc(len + 1)); memcpy(utf8Text, tmputf.c_str(), len); utf8Text[len] = '\0'; } @@ -1084,11 +1085,11 @@ static GType scintilla_object_accessible_get_type(GType parent_type G_GNUC_UNUSE static AtkObject *scintilla_object_accessible_new(GType parent_type, GObject *obj) { g_return_val_if_fail(SCINTILLA_IS_OBJECT(obj), nullptr); - AtkObject *accessible = (AtkObject *) g_object_new(scintilla_object_accessible_get_type(parent_type), + AtkObject *accessible = static_cast(g_object_new(scintilla_object_accessible_get_type(parent_type), #if HAVE_WIDGET_SET_UNSET "widget", obj, #endif - nullptr); + nullptr)); atk_object_initialize(accessible, obj); return accessible; diff --git a/scintilla/gtk/Wrappers.h b/scintilla/gtk/Wrappers.h new file mode 100644 index 0000000000..a22605a030 --- /dev/null +++ b/scintilla/gtk/Wrappers.h @@ -0,0 +1,109 @@ +// Scintilla source code edit control +// Wrappers.h - Encapsulation of GLib, GObject, Pango, Cairo, GTK, and GDK types +// Copyright 2022 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef WRAPPERS_H +#define WRAPPERS_H + +namespace Scintilla::Internal { + +// GLib + +struct GFreeReleaser { + template + void operator()(T *object) noexcept { + g_free(object); + } +}; + +using UniqueStr = std::unique_ptr; + +// GObject + +struct GObjectReleaser { + // Called by unique_ptr to destroy/free the object + template + void operator()(T *object) noexcept { + g_object_unref(object); + } +}; + +// Pango + +using UniquePangoContext = std::unique_ptr; +using UniquePangoLayout = std::unique_ptr; +using UniquePangoFontMap = std::unique_ptr; + +struct FontDescriptionReleaser { + void operator()(PangoFontDescription *fontDescription) noexcept { + pango_font_description_free(fontDescription); + } +}; + +using UniquePangoFontDescription = std::unique_ptr; + +struct FontMetricsReleaser { + void operator()(PangoFontMetrics *metrics) noexcept { + pango_font_metrics_unref(metrics); + } +}; + +using UniquePangoFontMetrics = std::unique_ptr; + +struct LayoutIterReleaser { + // Called by unique_ptr to destroy/free the object + void operator()(PangoLayoutIter *iter) noexcept { + pango_layout_iter_free(iter); + } +}; + +using UniquePangoLayoutIter = std::unique_ptr; + +// Cairo + +struct CairoReleaser { + void operator()(cairo_t *context) noexcept { + cairo_destroy(context); + } +}; + +using UniqueCairo = std::unique_ptr; + +struct CairoSurfaceReleaser { + void operator()(cairo_surface_t *psurf) noexcept { + cairo_surface_destroy(psurf); + } +}; + +using UniqueCairoSurface = std::unique_ptr; + +// GTK + +using UniqueIMContext = std::unique_ptr; + +// GDK + +struct GdkEventReleaser { + void operator()(GdkEvent *ev) noexcept { + gdk_event_free(ev); + } +}; + +using UniqueGdkEvent = std::unique_ptr; + +inline void UnRefCursor(GdkCursor *cursor) noexcept { +#if GTK_CHECK_VERSION(3,0,0) + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif +} + +[[nodiscard]] inline GdkWindow *WindowFromWidget(GtkWidget *w) noexcept { + return gtk_widget_get_window(w); +} + +} + +#endif diff --git a/scintilla/include/Scintilla.h b/scintilla/include/Scintilla.h index b46e886b60..5781674e33 100644 --- a/scintilla/include/Scintilla.h +++ b/scintilla/include/Scintilla.h @@ -57,6 +57,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_GETCURRENTPOS 2008 #define SCI_GETANCHOR 2009 #define SCI_GETSTYLEAT 2010 +#define SCI_GETSTYLEINDEXAT 2038 #define SCI_REDO 2011 #define SCI_SETUNDOCOLLECTION 2012 #define SCI_SELECTALL 2013 @@ -151,7 +152,12 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SC_MARK_RGBAIMAGE 30 #define SC_MARK_BOOKMARK 31 #define SC_MARK_VERTICALBOOKMARK 32 +#define SC_MARK_BAR 33 #define SC_MARK_CHARACTER 10000 +#define SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN 21 +#define SC_MARKNUM_HISTORY_SAVED 22 +#define SC_MARKNUM_HISTORY_MODIFIED 23 +#define SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED 24 #define SC_MARKNUM_FOLDEREND 25 #define SC_MARKNUM_FOLDEROPENMID 26 #define SC_MARKNUM_FOLDERMIDTAIL 27 @@ -352,6 +358,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define INDIC_POINTCHARACTER 19 #define INDIC_GRADIENT 20 #define INDIC_GRADIENTCENTRE 21 +#define INDIC_POINT_TOP 22 #define INDIC_CONTAINER 8 #define INDIC_IME 32 #define INDIC_IME_MAX 35 @@ -359,7 +366,15 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define INDICATOR_CONTAINER 8 #define INDICATOR_IME 32 #define INDICATOR_IME_MAX 35 -#define INDICATOR_MAX 35 +#define INDICATOR_HISTORY_REVERTED_TO_ORIGIN_INSERTION 36 +#define INDICATOR_HISTORY_REVERTED_TO_ORIGIN_DELETION 37 +#define INDICATOR_HISTORY_SAVED_INSERTION 38 +#define INDICATOR_HISTORY_SAVED_DELETION 39 +#define INDICATOR_HISTORY_MODIFIED_INSERTION 40 +#define INDICATOR_HISTORY_MODIFIED_DELETION 41 +#define INDICATOR_HISTORY_REVERTED_TO_MODIFIED_INSERTION 42 +#define INDICATOR_HISTORY_REVERTED_TO_MODIFIED_DELETION 43 +#define INDICATOR_MAX 43 #define SCI_INDICSETSTYLE 2080 #define SCI_INDICGETSTYLE 2081 #define SCI_INDICSETFORE 2082 @@ -473,7 +488,15 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCFIND_POSIX 0x00400000 #define SCFIND_CXX11REGEX 0x00800000 #define SCI_FINDTEXT 2150 +#define SCI_FINDTEXTFULL 2196 #define SCI_FORMATRANGE 2151 +#define SCI_FORMATRANGEFULL 2777 +#define SC_CHANGE_HISTORY_DISABLED 0 +#define SC_CHANGE_HISTORY_ENABLED 1 +#define SC_CHANGE_HISTORY_MARKERS 2 +#define SC_CHANGE_HISTORY_INDICATORS 4 +#define SCI_SETCHANGEHISTORY 2780 +#define SCI_GETCHANGEHISTORY 2781 #define SCI_GETFIRSTVISIBLELINE 2152 #define SCI_GETLINE 2153 #define SCI_GETLINECOUNT 2154 @@ -486,7 +509,9 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_SETSEL 2160 #define SCI_GETSELTEXT 2161 #define SCI_GETTEXTRANGE 2162 +#define SCI_GETTEXTRANGEFULL 2039 #define SCI_HIDESELECTION 2163 +#define SCI_GETSELECTIONHIDDEN 2088 #define SCI_POINTXFROMPOSITION 2164 #define SCI_POINTYFROMPOSITION 2165 #define SCI_LINEFROMPOSITION 2166 @@ -573,6 +598,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SC_FOLDACTION_CONTRACT 0 #define SC_FOLDACTION_EXPAND 1 #define SC_FOLDACTION_TOGGLE 2 +#define SC_FOLDACTION_CONTRACT_EVERY_LEVEL 4 #define SCI_FOLDLINE 2237 #define SCI_FOLDCHILDREN 2238 #define SCI_EXPANDCHILDREN 2239 @@ -924,6 +950,8 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_INDICATOREND 2509 #define SCI_SETPOSITIONCACHE 2514 #define SCI_GETPOSITIONCACHE 2515 +#define SCI_SETLAYOUTTHREADS 2775 +#define SCI_GETLAYOUTTHREADS 2776 #define SCI_COPYALLOWLINE 2519 #define SCI_GETCHARACTERPOINTER 2520 #define SCI_GETRANGEPOINTER 2643 @@ -1101,6 +1129,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SC_SUPPORTS_FRACTIONAL_STROKE_WIDTH 2 #define SC_SUPPORTS_TRANSLUCENT_STROKE 3 #define SC_SUPPORTS_PIXEL_MODIFICATION 4 +#define SC_SUPPORTS_THREAD_SAFE_MEASURE_WIDTHS 5 #define SCI_SUPPORTSFEATURE 2750 #define SC_LINECHARACTERINDEX_NONE 0 #define SC_LINECHARACTERINDEX_UTF32 1 @@ -1263,17 +1292,33 @@ struct Sci_CharacterRange { Sci_PositionCR cpMax; }; +struct Sci_CharacterRangeFull { + Sci_Position cpMin; + Sci_Position cpMax; +}; + struct Sci_TextRange { struct Sci_CharacterRange chrg; char *lpstrText; }; +struct Sci_TextRangeFull { + struct Sci_CharacterRangeFull chrg; + char *lpstrText; +}; + struct Sci_TextToFind { struct Sci_CharacterRange chrg; const char *lpstrText; struct Sci_CharacterRange chrgText; }; +struct Sci_TextToFindFull { + struct Sci_CharacterRangeFull chrg; + const char *lpstrText; + struct Sci_CharacterRangeFull chrgText; +}; + typedef void *Sci_SurfaceID; struct Sci_Rectangle { @@ -1294,6 +1339,14 @@ struct Sci_RangeToFormat { struct Sci_CharacterRange chrg; }; +struct Sci_RangeToFormatFull { + Sci_SurfaceID hdc; + Sci_SurfaceID hdcTarget; + struct Sci_Rectangle rc; + struct Sci_Rectangle rcPage; + struct Sci_CharacterRangeFull chrg; +}; + #ifndef __cplusplus /* For the GTK+ platform, g-ir-scanner needs to have these typedefs. This * is not required in C++ code and actually seems to break ScintillaEditPy */ diff --git a/scintilla/include/Scintilla.iface b/scintilla/include/Scintilla.iface index f2ef2d33e5..bd6cb76de1 100644 --- a/scintilla/include/Scintilla.iface +++ b/scintilla/include/Scintilla.iface @@ -60,9 +60,12 @@ ## cells -> pointer to array of cells, each cell containing a style byte and character byte ## pointer -> void* pointer that may point to a document, loader, internal text storage or similar ## textrange -> range of a min and a max position with an output string +## textrangefull -> range of a min and a max position with an output string - supports 64-bit ## findtext -> searchrange, text -> foundposition +## findtextfull -> searchrange, text -> foundposition ## keymod -> integer containing key in low half and modifiers in high half ## formatrange +## formatrangefull ## Enumeration types always start with a capital letter ## Types no longer used: ## findtextex -> searchrange @@ -130,6 +133,9 @@ get position GetAnchor=2009(,) # Returns the style byte at the position. get int GetStyleAt=2010(position pos,) +# Returns the unsigned style byte at the position. +get int GetStyleIndexAt=2038(position pos,) + # Redoes the next action on the undo history. fun void Redo=2011(,) @@ -352,6 +358,7 @@ val SC_MARK_UNDERLINE=29 val SC_MARK_RGBAIMAGE=30 val SC_MARK_BOOKMARK=31 val SC_MARK_VERTICALBOOKMARK=32 +val SC_MARK_BAR=33 val SC_MARK_CHARACTER=10000 @@ -379,7 +386,11 @@ ali SC_MARK_RGBAIMAGE=RGBA_IMAGE ali SC_MARK_VERTICALBOOKMARK=VERTICAL_BOOKMARK enu MarkerOutline=SC_MARKNUM_ -# Markers used for outlining column. +# Markers used for outlining and change history columns. +val SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN=21 +val SC_MARKNUM_HISTORY_SAVED=22 +val SC_MARKNUM_HISTORY_MODIFIED=23 +val SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED=24 val SC_MARKNUM_FOLDEREND=25 val SC_MARKNUM_FOLDEROPENMID=26 val SC_MARKNUM_FOLDERMIDTAIL=27 @@ -836,6 +847,7 @@ val INDIC_POINT=18 val INDIC_POINTCHARACTER=19 val INDIC_GRADIENT=20 val INDIC_GRADIENTCENTRE=21 +val INDIC_POINT_TOP=22 # INDIC_CONTAINER, INDIC_IME, INDIC_IME_MAX, and INDIC_MAX are indicator numbers, # not IndicatorStyles so should not really be in the INDIC_ enumeration. @@ -849,7 +861,15 @@ enu IndicatorNumbers=INDICATOR_ val INDICATOR_CONTAINER=8 val INDICATOR_IME=32 val INDICATOR_IME_MAX=35 -val INDICATOR_MAX=35 +val INDICATOR_HISTORY_REVERTED_TO_ORIGIN_INSERTION=36 +val INDICATOR_HISTORY_REVERTED_TO_ORIGIN_DELETION=37 +val INDICATOR_HISTORY_SAVED_INSERTION=38 +val INDICATOR_HISTORY_SAVED_DELETION=39 +val INDICATOR_HISTORY_MODIFIED_INSERTION=40 +val INDICATOR_HISTORY_MODIFIED_DELETION=41 +val INDICATOR_HISTORY_REVERTED_TO_MODIFIED_INSERTION=42 +val INDICATOR_HISTORY_REVERTED_TO_MODIFIED_DELETION=43 +val INDICATOR_MAX=43 ali INDIC_TT=T_T ali INDIC_ROUNDBOX=ROUND_BOX @@ -1209,9 +1229,27 @@ ali SCFIND_CXX11REGEX=CXX11_REG_EX # Find some text in the document. fun position FindText=2150(FindOption searchFlags, findtext ft) -# On Windows, will draw the document into a display context such as a printer. +# Find some text in the document. +fun position FindTextFull=2196(FindOption searchFlags, findtextfull ft) + +# Draw the document into a display context such as a printer. fun position FormatRange=2151(bool draw, formatrange fr) +# Draw the document into a display context such as a printer. +fun position FormatRangeFull=2777(bool draw, formatrangefull fr) + +enu ChangeHistoryOption=SC_CHANGE_HISTORY_ +val SC_CHANGE_HISTORY_DISABLED=0 +val SC_CHANGE_HISTORY_ENABLED=1 +val SC_CHANGE_HISTORY_MARKERS=2 +val SC_CHANGE_HISTORY_INDICATORS=4 + +# Enable or disable change history. +set void SetChangeHistory=2780(ChangeHistoryOption changeHistory,) + +# Report change history status. +get ChangeHistoryOption GetChangeHistory=2781(,) + # Retrieve the display line at the top of the display. get line GetFirstVisibleLine=2152(,) @@ -1252,9 +1290,16 @@ fun position GetSelText=2161(, stringresult text) # Return the length of the text. fun position GetTextRange=2162(, textrange tr) +# Retrieve a range of text that can be past 2GB. +# Return the length of the text. +fun position GetTextRangeFull=2039(, textrangefull tr) + # Draw the selection either highlighted or in normal (non-highlighted) style. fun void HideSelection=2163(bool hide,) +#Is the selection visible or hidden? +get bool GetSelectionHidden=2088(,) + # Retrieve the x value of the point in the window where a position is displayed. fun int PointXFromPosition=2164(, position pos) @@ -1518,6 +1563,7 @@ enu FoldAction=SC_FOLDACTION_ val SC_FOLDACTION_CONTRACT=0 val SC_FOLDACTION_EXPAND=1 val SC_FOLDACTION_TOGGLE=2 +val SC_FOLDACTION_CONTRACT_EVERY_LEVEL=4 # Expand or contract a fold header. fun void FoldLine=2237(line line, FoldAction action) @@ -2535,6 +2581,12 @@ set void SetPositionCache=2514(int size,) # How many entries are allocated to the position cache? get int GetPositionCache=2515(,) +# Set maximum number of threads used for layout +set void SetLayoutThreads=2775(int threads,) + +# Get maximum number of threads used for layout +get int GetLayoutThreads=2776(,) + # Copy the selection, if selection empty copy the line with the caret fun void CopyAllowLine=2519(,) @@ -2924,13 +2976,13 @@ get Technology GetTechnology=2631(,) # Create an ILoader*. fun pointer CreateLoader=2632(position bytes, DocumentOption documentOptions) -# On OS X, show a find indicator. +# On macOS, show a find indicator. fun void FindIndicatorShow=2640(position start, position end) -# On OS X, flash a find indicator, then fade out. +# On macOS, flash a find indicator, then fade out. fun void FindIndicatorFlash=2641(position start, position end) -# On OS X, hide the find indicator. +# On macOS, hide the find indicator. fun void FindIndicatorHide=2642(,) # Move caret to before first visible character on display line. @@ -3040,6 +3092,7 @@ val SC_SUPPORTS_PIXEL_DIVISIONS=1 val SC_SUPPORTS_FRACTIONAL_STROKE_WIDTH=2 val SC_SUPPORTS_TRANSLUCENT_STROKE=3 val SC_SUPPORTS_PIXEL_MODIFICATION=4 +val SC_SUPPORTS_THREAD_SAFE_MEASURE_WIDTHS=5 # Get whether a feature is supported get bool SupportsFeature=2750(Supports feature,) diff --git a/scintilla/include/ScintillaCall.h b/scintilla/include/ScintillaCall.h index d16f2aff07..0e79e37db2 100644 --- a/scintilla/include/ScintillaCall.h +++ b/scintilla/include/ScintillaCall.h @@ -68,6 +68,7 @@ class ScintillaCall { char CharacterAt(Position position); int UnsignedStyleAt(Position position); std::string StringOfSpan(Span span); + std::string StringOfRange(Span span); Position ReplaceTarget(std::string_view text); Position ReplaceTargetRE(std::string_view text); Position SearchInTarget(std::string_view text); @@ -88,6 +89,7 @@ class ScintillaCall { Position CurrentPos(); Position Anchor(); int StyleAt(Position pos); + int StyleIndexAt(Position pos); void Redo(); void SetUndoCollection(bool collectUndo); void SelectAll(); @@ -321,7 +323,11 @@ class ScintillaCall { void SetPrintColourMode(Scintilla::PrintOption mode); Scintilla::PrintOption PrintColourMode(); Position FindText(Scintilla::FindOption searchFlags, void *ft); + Position FindTextFull(Scintilla::FindOption searchFlags, void *ft); Position FormatRange(bool draw, void *fr); + Position FormatRangeFull(bool draw, void *fr); + void SetChangeHistory(Scintilla::ChangeHistoryOption changeHistory); + Scintilla::ChangeHistoryOption ChangeHistory(); Line FirstVisibleLine(); Position GetLine(Line line, char *text); std::string GetLine(Line line); @@ -336,7 +342,9 @@ class ScintillaCall { Position GetSelText(char *text); std::string GetSelText(); Position GetTextRange(void *tr); + Position GetTextRangeFull(void *tr); void HideSelection(bool hide); + bool SelectionHidden(); int PointXFromPosition(Position pos); int PointYFromPosition(Position pos); Line LineFromPosition(Position pos); @@ -682,6 +690,8 @@ class ScintillaCall { Position IndicatorEnd(int indicator, Position pos); void SetPositionCache(int size); int PositionCache(); + void SetLayoutThreads(int threads); + int LayoutThreads(); void CopyAllowLine(); void *CharacterPointer(); void *RangePointer(Position start, Position lengthRange); diff --git a/scintilla/include/ScintillaMessages.h b/scintilla/include/ScintillaMessages.h index 95ed09530e..663d6e12c7 100644 --- a/scintilla/include/ScintillaMessages.h +++ b/scintilla/include/ScintillaMessages.h @@ -28,6 +28,7 @@ enum class Message { GetCurrentPos = 2008, GetAnchor = 2009, GetStyleAt = 2010, + GetStyleIndexAt = 2038, Redo = 2011, SetUndoCollection = 2012, SelectAll = 2013, @@ -257,7 +258,11 @@ enum class Message { SetPrintColourMode = 2148, GetPrintColourMode = 2149, FindText = 2150, + FindTextFull = 2196, FormatRange = 2151, + FormatRangeFull = 2777, + SetChangeHistory = 2780, + GetChangeHistory = 2781, GetFirstVisibleLine = 2152, GetLine = 2153, GetLineCount = 2154, @@ -270,7 +275,9 @@ enum class Message { SetSel = 2160, GetSelText = 2161, GetTextRange = 2162, + GetTextRangeFull = 2039, HideSelection = 2163, + GetSelectionHidden = 2088, PointXFromPosition = 2164, PointYFromPosition = 2165, LineFromPosition = 2166, @@ -607,6 +614,8 @@ enum class Message { IndicatorEnd = 2509, SetPositionCache = 2514, GetPositionCache = 2515, + SetLayoutThreads = 2775, + GetLayoutThreads = 2776, CopyAllowLine = 2519, GetCharacterPointer = 2520, GetRangePointer = 2643, diff --git a/scintilla/include/ScintillaStructures.h b/scintilla/include/ScintillaStructures.h index 6bd16e8c1b..4d13a4367e 100644 --- a/scintilla/include/ScintillaStructures.h +++ b/scintilla/include/ScintillaStructures.h @@ -19,17 +19,33 @@ struct CharacterRange { PositionCR cpMax; }; +struct CharacterRangeFull { + Position cpMin; + Position cpMax; +}; + struct TextRange { CharacterRange chrg; char *lpstrText; }; +struct TextRangeFull { + CharacterRangeFull chrg; + char *lpstrText; +}; + struct TextToFind { CharacterRange chrg; const char *lpstrText; CharacterRange chrgText; }; +struct TextToFindFull { + CharacterRangeFull chrg; + const char *lpstrText; + CharacterRangeFull chrgText; +}; + using SurfaceID = void *; struct Rectangle { @@ -49,6 +65,14 @@ struct RangeToFormat { CharacterRange chrg; }; +struct RangeToFormatFull { + SurfaceID hdc; + SurfaceID hdcTarget; + Rectangle rc; + Rectangle rcPage; + CharacterRangeFull chrg; +}; + struct NotifyHeader { /* Compatible with Windows NMHDR. * hwndFrom is really an environment specific window handle or pointer @@ -58,6 +82,8 @@ struct NotifyHeader { Notification code; }; +enum class Message; // Declare in case ScintillaMessages.h not included + struct NotificationData { NotifyHeader nmhdr; Position position; diff --git a/scintilla/include/ScintillaTypes.h b/scintilla/include/ScintillaTypes.h index 9178784744..5fc18b71b3 100644 --- a/scintilla/include/ScintillaTypes.h +++ b/scintilla/include/ScintillaTypes.h @@ -86,10 +86,15 @@ enum class MarkerSymbol { RgbaImage = 30, Bookmark = 31, VerticalBookmark = 32, + Bar = 33, Character = 10000, }; enum class MarkerOutline { + HistoryRevertedToOrigin = 21, + HistorySaved = 22, + HistoryModified = 23, + HistoryRevertedToModified = 24, FolderEnd = 25, FolderOpenMid = 26, FolderMidTail = 27, @@ -213,13 +218,22 @@ enum class IndicatorStyle { PointCharacter = 19, Gradient = 20, GradientCentre = 21, + PointTop = 22, }; enum class IndicatorNumbers { Container = 8, Ime = 32, ImeMax = 35, - Max = 35, + HistoryRevertedToOriginInsertion = 36, + HistoryRevertedToOriginDeletion = 37, + HistorySavedInsertion = 38, + HistorySavedDeletion = 39, + HistoryModifiedInsertion = 40, + HistoryModifiedDeletion = 41, + HistoryRevertedToModifiedInsertion = 42, + HistoryRevertedToModifiedDeletion = 43, + Max = 43, }; enum class IndicValue { @@ -263,6 +277,13 @@ enum class FindOption { Cxx11RegEx = 0x00800000, }; +enum class ChangeHistoryOption { + Disabled = 0, + Enabled = 1, + Markers = 2, + Indicators = 4, +}; + enum class FoldLevel { None = 0x0, Base = 0x400, @@ -281,6 +302,7 @@ enum class FoldAction { Contract = 0, Expand = 1, Toggle = 2, + ContractEveryLevel = 4, }; enum class AutomaticFold { @@ -507,6 +529,7 @@ enum class Supports { FractionalStrokeWidth = 2, TranslucentStroke = 3, PixelModification = 4, + ThreadSafeMeasureWidths = 5, }; enum class LineCharacterIndexType { @@ -795,6 +818,15 @@ constexpr KeyMod operator&(KeyMod a, KeyMod b) noexcept { return static_cast(static_cast(a) & static_cast(b)); } +constexpr KeyMod ModifierFlags(bool shift, bool ctrl, bool alt, bool meta=false, bool super=false) noexcept { + return + (shift ? KeyMod::Shift : KeyMod::Norm) | + (ctrl ? KeyMod::Ctrl : KeyMod::Norm) | + (alt ? KeyMod::Alt : KeyMod::Norm) | + (meta ? KeyMod::Meta : KeyMod::Norm) | + (super ? KeyMod::Super : KeyMod::Norm); +} + // Test if an enum class value has some bit flag(s) of test set. template constexpr bool FlagSet(T value, T test) { diff --git a/scintilla/lexilla/include/SciLexer.h b/scintilla/lexilla/include/SciLexer.h index c9a076b96d..4db52d8193 100644 --- a/scintilla/lexilla/include/SciLexer.h +++ b/scintilla/lexilla/include/SciLexer.h @@ -169,6 +169,7 @@ #define SCE_P_FCHARACTER 17 #define SCE_P_FTRIPLE 18 #define SCE_P_FTRIPLEDOUBLE 19 +#define SCE_P_ATTRIBUTE 20 #define SCE_C_DEFAULT 0 #define SCE_C_COMMENT 1 #define SCE_C_COMMENTLINE 2 diff --git a/scintilla/lexilla/lexers/LexBash.cxx b/scintilla/lexilla/lexers/LexBash.cxx index be66ad9f43..a071e62493 100644 --- a/scintilla/lexilla/lexers/LexBash.cxx +++ b/scintilla/lexilla/lexers/LexBash.cxx @@ -609,7 +609,7 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int sc.SetState(SCE_SH_DEFAULT); break; case SCE_SH_COMMENTLINE: - if (sc.atLineEnd && sc.chPrev != '\\') { + if (sc.MatchLineEnd() && sc.chPrev != '\\') { sc.SetState(SCE_SH_DEFAULT); } break; @@ -678,13 +678,13 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int if (sc.atLineStart) { sc.SetState(SCE_SH_HERE_Q); int prefixws = 0; - while (sc.ch == '\t' && !sc.atLineEnd) { // tabulation prefix + while (sc.ch == '\t' && !sc.MatchLineEnd()) { // tabulation prefix sc.Forward(); prefixws++; } if (prefixws > 0) sc.SetState(SCE_SH_HERE_Q); - while (!sc.atLineEnd) { + while (!sc.MatchLineEnd()) { sc.Forward(); } char s[HERE_DELIM_MAX]; @@ -794,7 +794,7 @@ void SCI_METHOD LexerBash::Lex(Sci_PositionU startPos, Sci_Position length, int } // Must check end of HereDoc state 1 before default state is handled - if (HereDoc.State == 1 && sc.atLineEnd) { + if (HereDoc.State == 1 && sc.MatchLineEnd()) { // Begin of here-doc (the line after the here-doc delimiter): // Lexically, the here-doc starts from the next line after the >>, but the // first line of here-doc seem to follow the style of the last EOL sequence diff --git a/scintilla/lexilla/lexers/LexBatch.cxx b/scintilla/lexilla/lexers/LexBatch.cxx index bdab686fbd..e0ec26dfa5 100644 --- a/scintilla/lexilla/lexers/LexBatch.cxx +++ b/scintilla/lexilla/lexers/LexBatch.cxx @@ -194,9 +194,12 @@ void ColouriseBatchDoc( Sci_PositionU wbo = 0; // Word Buffer Offset - also Special Keyword Buffer Length // Check for Comment - return if found - if ((CompareCaseInsensitive(wordBuffer, "rem") == 0) && continueProcessing) { - styler.ColourTo(endPos, SCE_BAT_COMMENT); - break; + if (continueProcessing) { + if ((CompareCaseInsensitive(wordBuffer, "rem") == 0) || (wordBuffer[0] == ':' && wordBuffer[1] == ':')) { + styler.ColourTo(startLine + offset - strlen(wordBuffer) - 1, SCE_BAT_DEFAULT); + styler.ColourTo(endPos, SCE_BAT_COMMENT); + break; + } } // Check for Separator if (IsBSeparator(wordBuffer[0])) { diff --git a/scintilla/lexilla/lexers/LexCPP.cxx b/scintilla/lexilla/lexers/LexCPP.cxx index 050a4b9268..1bf5b9551b 100644 --- a/scintilla/lexilla/lexers/LexCPP.cxx +++ b/scintilla/lexilla/lexers/LexCPP.cxx @@ -99,8 +99,10 @@ bool OnlySpaceOrTab(const std::string &s) noexcept { return true; } -std::vector StringSplit(const std::string &text, int separator) { - std::vector vs(text.empty() ? 0 : 1); +using Tokens = std::vector; + +Tokens StringSplit(const std::string &text, int separator) { + Tokens vs(text.empty() ? 0 : 1); for (const char ch : text) { if (ch == separator) { vs.emplace_back(); @@ -112,33 +114,26 @@ std::vector StringSplit(const std::string &text, int separator) { } struct BracketPair { - std::vector::iterator itBracket; - std::vector::iterator itEndBracket; + Tokens::iterator itBracket; + Tokens::iterator itEndBracket; }; -BracketPair FindBracketPair(std::vector &tokens) { - BracketPair bp; - std::vector::iterator itTok = std::find(tokens.begin(), tokens.end(), "("); - bp.itBracket = tokens.end(); - bp.itEndBracket = tokens.end(); - if (itTok != tokens.end()) { - bp.itBracket = itTok; +BracketPair FindBracketPair(Tokens &tokens) { + const Tokens::iterator itBracket = std::find(tokens.begin(), tokens.end(), "("); + if (itBracket != tokens.end()) { size_t nest = 0; - while (itTok != tokens.end()) { + for (Tokens::iterator itTok = itBracket; itTok != tokens.end(); ++itTok) { if (*itTok == "(") { nest++; } else if (*itTok == ")") { nest--; if (nest == 0) { - bp.itEndBracket = itTok; - return bp; + return { itBracket, itTok }; } } - ++itTok; } } - bp.itBracket = tokens.end(); - return bp; + return { tokens.end(), tokens.end() }; } void highlightTaskMarker(StyleContext &sc, LexAccessor &styler, @@ -174,7 +169,7 @@ class EscapeSequence { int digitsLeft = 0; public: EscapeSequence() = default; - void resetEscapeState(int nextChar) { + void resetEscapeState(int nextChar) noexcept { digitsLeft = 0; escapeSetValid = &setNoneNumeric; if (nextChar == 'U') { @@ -191,7 +186,7 @@ class EscapeSequence { escapeSetValid = &setOctDigits; } } - bool atEscapeEnd(int currChar) const { + bool atEscapeEnd(int currChar) const noexcept { return (digitsLeft <= 0) || !escapeSetValid->Contains(currChar); } void consumeDigit() noexcept { @@ -268,8 +263,7 @@ class LinePPState { } } public: - LinePPState() noexcept { - } + LinePPState() noexcept = default; bool ValidLevel() const noexcept { return level >= 0 && level < maximumNestingLevel; } @@ -320,9 +314,8 @@ class PPStates { LinePPState ForLine(Sci_Position line) const noexcept { if ((line > 0) && (vlls.size() > static_cast(line))) { return vlls[line]; - } else { - return LinePPState(); } + return {}; } void Add(Sci_Position line, LinePPState lls) { vlls.resize(line+1); @@ -659,7 +652,7 @@ class LexerCPP : public ILexer5 { if (styleActive < sizeLexicalClasses) returnBuffer += lexicalClasses[styleActive].tags; else - returnBuffer = ""; + returnBuffer.clear(); return returnBuffer.c_str(); } return ""; @@ -691,8 +684,8 @@ class LexerCPP : public ILexer5 { constexpr static int MaskActive(int style) noexcept { return style & ~inactiveFlag; } - void EvaluateTokens(std::vector &tokens, const SymbolTable &preprocessorDefinitions); - std::vector Tokenize(const std::string &expr) const; + void EvaluateTokens(Tokens &tokens, const SymbolTable &preprocessorDefinitions); + Tokens Tokenize(const std::string &expr) const; bool EvaluateExpression(const std::string &expr, const SymbolTable &preprocessorDefinitions); }; @@ -734,6 +727,8 @@ Sci_Position SCI_METHOD LexerCPP::WordListSet(int n, const char *wl) { case 5: wordListN = &markerList; break; + default: + break; } Sci_Position firstModification = -1; if (wordListN) { @@ -776,14 +771,14 @@ Sci_Position SCI_METHOD LexerCPP::WordListSet(int n, const char *wl) { void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) { LexAccessor styler(pAccess); - CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-"); - CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-"); + const CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-"); + const CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-"); - CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]"); + const CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]"); setWordStart = CharacterSet(CharacterSet::setAlpha, "_", true); - CharacterSet setInvalidRawFirst(CharacterSet::setNone, " )\\\t\v\f\n"); + const CharacterSet setInvalidRawFirst(CharacterSet::setNone, " )\\\t\v\f\n"); if (options.identifiersAllowDollars) { setWordStart.Add('$'); @@ -833,7 +828,8 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i if (!options.updatePreprocessor) ppDefineHistory.clear(); - std::vector::iterator itInvalid = std::find_if(ppDefineHistory.begin(), ppDefineHistory.end(), + const std::vector::iterator itInvalid = std::find_if( + ppDefineHistory.begin(), ppDefineHistory.end(), [lineCurrent](const PPDefinition &p) noexcept { return p.line >= lineCurrent; }); if (itInvalid != ppDefineHistory.end()) { ppDefineHistory.erase(itInvalid, ppDefineHistory.end()); @@ -887,7 +883,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i lineCurrent++; lineEndNext = styler.LineEnd(lineCurrent); vlls.Add(lineCurrent, preproc); - if (rawStringTerminator != "") { + if (!rawStringTerminator.empty()) { rawSTNew.Set(lineCurrent-1, rawStringTerminator); } } @@ -898,7 +894,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i lineCurrent++; lineEndNext = styler.LineEnd(lineCurrent); vlls.Add(lineCurrent, preproc); - if (rawStringTerminator != "") { + if (!rawStringTerminator.empty()) { rawSTNew.Set(lineCurrent-1, rawStringTerminator); } sc.Forward(); @@ -950,7 +946,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i } else if (keywords4.InList(s)) { sc.ChangeState(SCE_C_GLOBALCLASS|activitySet); } else { - int subStyle = classifierIdentifiers.ValueFor(s); + const int subStyle = classifierIdentifiers.ValueFor(s); if (subStyle >= 0) { sc.ChangeState(subStyle|activitySet); } @@ -1083,7 +1079,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i if (!(IsASpace(sc.ch) || (sc.ch == 0))) { sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet); } else if (!keywords3.InList(s + 1) && !keywords3.InList(s)) { - int subStyleCDKW = classifierDocKeyWords.ValueFor(s+1); + const int subStyleCDKW = classifierDocKeyWords.ValueFor(s+1); if (subStyleCDKW >= 0) { sc.ChangeState(subStyleCDKW|activitySet); } else { @@ -1100,7 +1096,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i sc.GetCurrentLowered(s, sizeof(s)); } if (!keywords3.InList(s)) { - int subStyleCDKW = classifierDocKeyWords.ValueFor(s + 1); + const int subStyleCDKW = classifierDocKeyWords.ValueFor(s + 1); if (subStyleCDKW >= 0) { sc.ChangeState(subStyleCDKW | activitySet); } else { @@ -1165,7 +1161,7 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i for (size_t termPos=rawStringTerminator.size(); termPos; termPos--) sc.Forward(); sc.SetState(SCE_C_DEFAULT|activitySet); - rawStringTerminator = ""; + rawStringTerminator.clear(); } break; case SCE_C_CHARACTER: @@ -1333,11 +1329,11 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i if (sc.Match("ifdef") || sc.Match("ifndef")) { const bool isIfDef = sc.Match("ifdef"); const int startRest = isIfDef ? 5 : 6; - std::string restOfLine = GetRestOfLine(styler, sc.currentPos + startRest + 1, false); - bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end(); + const std::string restOfLine = GetRestOfLine(styler, sc.currentPos + startRest + 1, false); + const bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end(); preproc.StartSection(isIfDef == foundDef); } else if (sc.Match("if")) { - std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true); + const std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true); const bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions); preproc.StartSection(ifGood); } else if (sc.Match("else")) { @@ -1431,8 +1427,8 @@ void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int i } else if (sc.Match("undef")) { if (options.updatePreprocessor && preproc.IsActive()) { const std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 5, false); - std::vector tokens = Tokenize(restOfLine); - if (tokens.size() >= 1) { + Tokens tokens = Tokenize(restOfLine); + if (!tokens.empty()) { const std::string key = tokens[0]; preprocessorDefinitions.erase(key); ppDefineHistory.push_back(PPDefinition(lineCurrent, key, "", true)); @@ -1587,7 +1583,7 @@ void SCI_METHOD LexerCPP::Fold(Sci_PositionU startPos, Sci_Position length, int } } -void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTable &preprocessorDefinitions) { +void LexerCPP::EvaluateTokens(Tokens &tokens, const SymbolTable &preprocessorDefinitions) { // Remove whitespace tokens tokens.erase(std::remove_if(tokens.begin(), tokens.end(), OnlySpaceOrTab), tokens.end()); @@ -1602,7 +1598,7 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 3); } else if (((i+3)) - SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i+2]); + const SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i+2]); if (it != preprocessorDefinitions.end()) { val = "1"; } @@ -1613,7 +1609,7 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl } } else { // defined - SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i+1]); + const SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i+1]); if (it != preprocessorDefinitions.end()) { val = "1"; } @@ -1631,14 +1627,14 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl for (size_t i = 0; (i macroTokens = Tokenize(it->second.value); + Tokens macroTokens = Tokenize(it->second.value); if (it->second.IsMacro()) { if ((i + 1 < tokens.size()) && (tokens.at(i + 1) == "(")) { // Create map of argument name to value - std::vector argumentNames = StringSplit(it->second.arguments, ','); + const Tokens argumentNames = StringSplit(it->second.arguments, ','); std::map arguments; size_t arg = 0; size_t tok = i+2; @@ -1658,7 +1654,8 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl for (size_t iMacro = 0; iMacro < macroTokens.size();) { if (setWordStart.Contains(macroTokens[iMacro][0])) { - std::map::const_iterator itFind = arguments.find(macroTokens[iMacro]); + const std::map::const_iterator itFind = + arguments.find(macroTokens[iMacro]); if (itFind != arguments.end()) { // TODO: Possible that value will be expression so should insert tokenized form macroTokens[iMacro] = itFind->second; @@ -1691,7 +1688,7 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl // Find bracketed subexpressions and recurse on them BracketPair bracketPair = FindBracketPair(tokens); while (bracketPair.itBracket != tokens.end()) { - std::vector inBracket(bracketPair.itBracket + 1, bracketPair.itEndBracket); + Tokens inBracket(bracketPair.itBracket + 1, bracketPair.itEndBracket); EvaluateTokens(inBracket, preprocessorDefinitions); // The insertion is done before the removal because there were failures with the opposite approach @@ -1709,7 +1706,7 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl int isTrue = atoi(tokens[j+1].c_str()); if (tokens[j] == "!") isTrue = !isTrue; - std::vector::iterator itInsert = + const Tokens::iterator itInsert = tokens.erase(tokens.begin() + j, tokens.begin() + j + 2); tokens.insert(itInsert, isTrue ? "1" : "0"); } else { @@ -1759,7 +1756,7 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl result = valA || valB; else if (tokens[k+1] == "&&") result = valA && valB; - std::vector::iterator itInsert = + const Tokens::iterator itInsert = tokens.erase(tokens.begin() + k, tokens.begin() + k + 3); tokens.insert(itInsert, std::to_string(result)); } else { @@ -1769,9 +1766,9 @@ void LexerCPP::EvaluateTokens(std::vector &tokens, const SymbolTabl } } -std::vector LexerCPP::Tokenize(const std::string &expr) const { +Tokens LexerCPP::Tokenize(const std::string &expr) const { // Break into tokens - std::vector tokens; + Tokens tokens; const char *cp = expr.c_str(); while (*cp) { std::string word; @@ -1811,13 +1808,13 @@ std::vector LexerCPP::Tokenize(const std::string &expr) const { } bool LexerCPP::EvaluateExpression(const std::string &expr, const SymbolTable &preprocessorDefinitions) { - std::vector tokens = Tokenize(expr); + Tokens tokens = Tokenize(expr); EvaluateTokens(tokens, preprocessorDefinitions); // "0" or "" -> false else true const bool isFalse = tokens.empty() || - ((tokens.size() == 1) && ((tokens[0] == "") || tokens[0] == "0")); + ((tokens.size() == 1) && (tokens[0].empty() || tokens[0] == "0")); return !isFalse; } diff --git a/scintilla/lexilla/lexers/LexCmake.cxx b/scintilla/lexilla/lexers/LexCmake.cxx index 5302c20969..1df2291527 100644 --- a/scintilla/lexilla/lexers/LexCmake.cxx +++ b/scintilla/lexilla/lexers/LexCmake.cxx @@ -88,7 +88,7 @@ static int calculateFoldCmake(Sci_PositionU start, Sci_PositionU end, int foldle if ( CompareCaseInsensitive(s, "IF") == 0 || CompareCaseInsensitive(s, "WHILE") == 0 || CompareCaseInsensitive(s, "MACRO") == 0 || CompareCaseInsensitive(s, "FOREACH") == 0 - || CompareCaseInsensitive(s, "FUNCTION") == 0 || CompareCaseInsensitive(s, "ELSEIF") == 0) + || CompareCaseInsensitive(s, "FUNCTION") == 0) newFoldlevel++; else if ( CompareCaseInsensitive(s, "ENDIF") == 0 || CompareCaseInsensitive(s, "ENDWHILE") == 0 || CompareCaseInsensitive(s, "ENDMACRO") == 0 || CompareCaseInsensitive(s, "ENDFOREACH") == 0 diff --git a/scintilla/lexilla/lexers/LexGDScript.cxx b/scintilla/lexilla/lexers/LexGDScript.cxx index 524d9226ee..e20ddb02a4 100644 --- a/scintilla/lexilla/lexers/LexGDScript.cxx +++ b/scintilla/lexilla/lexers/LexGDScript.cxx @@ -51,7 +51,6 @@ bool IsGDComment(Accessor &styler, Sci_Position pos, Sci_Position len) { } constexpr bool IsGDSingleQuoteStringState(int st) noexcept { - return ((st == SCE_GD_CHARACTER) || (st == SCE_GD_STRING)); } diff --git a/scintilla/lexilla/lexers/LexHTML.cxx b/scintilla/lexilla/lexers/LexHTML.cxx index 44f75db8de..8540718379 100644 --- a/scintilla/lexilla/lexers/LexHTML.cxx +++ b/scintilla/lexilla/lexers/LexHTML.cxx @@ -99,15 +99,13 @@ script_type segIsScriptingIndicator(Accessor &styler, Sci_PositionU start, Sci_P char s[100]; GetTextSegment(styler, start, end, s, sizeof(s)); //Platform::DebugPrintf("Scripting indicator [%s]\n", s); - if (strstr(s, "src")) // External script - return eScriptNone; if (strstr(s, "vbs")) return eScriptVBS; if (strstr(s, "pyth")) return eScriptPython; - if (strstr(s, "javas")) - return eScriptJS; - if (strstr(s, "jscr")) + // https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type + // https://mimesniff.spec.whatwg.org/#javascript-mime-type + if (strstr(s, "javas") || strstr(s, "ecmas") || strstr(s, "module") || strstr(s, "jscr")) return eScriptJS; if (strstr(s, "php")) return eScriptPHP; @@ -183,9 +181,8 @@ int stateForPrintState(int StateToPrint) { return state; } -inline bool IsNumber(Sci_PositionU start, Accessor &styler) { - return IsADigit(styler[start]) || (styler[start] == '.') || - (styler[start] == '-') || (styler[start] == '#'); +constexpr bool IsNumberChar(char ch) noexcept { + return IsADigit(ch) || ch == '.' || ch == '-' || ch == '#'; } inline bool isStringState(int state) { @@ -254,20 +251,27 @@ inline bool isCommentASPState(int state) { return bResult; } -void classifyAttribHTML(Sci_PositionU start, Sci_PositionU end, const WordList &keywords, Accessor &styler) { - const bool wordIsNumber = IsNumber(start, styler); +bool classifyAttribHTML(script_mode inScriptType, Sci_PositionU start, Sci_PositionU end, const WordList &keywords, Accessor &styler) { char chAttr = SCE_H_ATTRIBUTEUNKNOWN; - if (wordIsNumber) { + bool isLanguageType = false; + if (IsNumberChar(styler[start])) { chAttr = SCE_H_NUMBER; } else { - std::string s = GetStringSegment(styler, start, end); + const std::string s = GetStringSegment(styler, start, end); if (keywords.InList(s.c_str())) chAttr = SCE_H_ATTRIBUTE; + if (inScriptType == eNonHtmlScript) { + // see https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model + if (s == "type" || s == "language") { + isLanguageType = true; + } + } } if ((chAttr == SCE_H_ATTRIBUTEUNKNOWN) && !keywords) // No keywords -> all are known chAttr = SCE_H_ATTRIBUTE; styler.ColourTo(end, chAttr); + return isLanguageType; } // https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-core-concepts @@ -1129,6 +1133,7 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int script_type aspScript = static_cast((lineState >> 4) & 0x0F); // 4 bits of script name script_type clientScript = static_cast((lineState >> 8) & 0x0F); // 4 bits of script name int beforePreProc = (lineState >> 12) & 0xFF; // 8 bits of state + bool isLanguageType = (lineState >> 20) & 1; // type or language attribute for script tag script_type scriptLanguage = ScriptOfState(state); // If eNonHtmlScript coincides with SCE_H_COMMENT, assume eScriptComment @@ -1210,7 +1215,9 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int case eScriptPHP: //not currently supported case eScriptVBS: - if ((state != SCE_HPHP_COMMENT) && (state != SCE_HPHP_COMMENTLINE) && (state != SCE_HJ_COMMENT) && (state != SCE_HJ_COMMENTLINE) && (state != SCE_HJ_COMMENTDOC) && (!isStringState(state))) { + if (!(state == SCE_HPHP_COMMENT || state == SCE_HPHP_COMMENTLINE) && + !(state == SCE_HJ_REGEX || state == SCE_HJ_COMMENT || state == SCE_HJ_COMMENTLINE || state == SCE_HJ_COMMENTDOC) && + !isStringState(state)) { //Platform::DebugPrintf("state=%d, StateToPrint=%d, initStyle=%d\n", state, StateToPrint, initStyle); //if ((state == SCE_HPHP_OPERATOR) || (state == SCE_HPHP_DEFAULT) || (state == SCE_HJ_SYMBOLS) || (state == SCE_HJ_START) || (state == SCE_HJ_DEFAULT)) { if (ch == '#') { @@ -1226,7 +1233,7 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int } else if ((ch == '{') || (ch == '}') || (foldComment && (ch == '/') && (chNext == '*'))) { levelCurrent += (((ch == '{') || (ch == '/')) ? 1 : -1); } - } else if (((state == SCE_HPHP_COMMENT) || (state == SCE_HJ_COMMENT)) && foldComment && (ch == '*') && (chNext == '/')) { + } else if (((state == SCE_HPHP_COMMENT) || (state == SCE_HJ_COMMENT || state == SCE_HJ_COMMENTDOC)) && foldComment && (ch == '*') && (chNext == '/')) { levelCurrent--; } break; @@ -1282,7 +1289,8 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int ((tagClosing ? 1 : 0) << 3) | ((aspScript & 0x0F) << 4) | ((clientScript & 0x0F) << 8) | - ((beforePreProc & 0xFF) << 12)); + ((beforePreProc & 0xFF) << 12) | + ((isLanguageType ? 1 : 0) << 20)); lineCurrent++; lineStartVisibleChars = 0; } @@ -1355,6 +1363,7 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int inScriptType = eHtml; scriptLanguage = eScriptNone; clientScript = eScriptJS; + isLanguageType = false; i += 2; visibleChars += 2; tagClosing = true; @@ -1880,6 +1889,7 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int } else { scriptLanguage = eScriptNone; } + isLanguageType = false; eClass = SCE_H_TAG; } if (ch == '>') { @@ -1925,14 +1935,7 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int break; case SCE_H_ATTRIBUTE: if (!setAttributeContinue.Contains(ch)) { - if (inScriptType == eNonHtmlScript) { - const int scriptLanguagePrev = scriptLanguage; - clientScript = segIsScriptingIndicator(styler, styler.GetStartSegment(), i - 1, scriptLanguage); - scriptLanguage = clientScript; - if ((scriptLanguagePrev != scriptLanguage) && (scriptLanguage == eScriptNone)) - inScriptType = eHtml; - } - classifyAttribHTML(styler.GetStartSegment(), i - 1, keywords, styler); + isLanguageType = classifyAttribHTML(inScriptType, styler.GetStartSegment(), i - 1, keywords, styler); if (ch == '>') { styler.ColourTo(i, SCE_H_TAG); if (inScriptType == eNonHtmlScript) { @@ -2007,8 +2010,10 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int break; case SCE_H_DOUBLESTRING: if (ch == '\"') { - if (inScriptType == eNonHtmlScript) { + if (isLanguageType) { scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage); + clientScript = scriptLanguage; + isLanguageType = false; } styler.ColourTo(i, SCE_H_DOUBLESTRING); state = SCE_H_OTHER; @@ -2016,8 +2021,10 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int break; case SCE_H_SINGLESTRING: if (ch == '\'') { - if (inScriptType == eNonHtmlScript) { + if (isLanguageType) { scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage); + clientScript = scriptLanguage; + isLanguageType = false; } styler.ColourTo(i, SCE_H_SINGLESTRING); state = SCE_H_OTHER; @@ -2031,7 +2038,7 @@ void SCI_METHOD LexerHTML::Lex(Sci_PositionU startPos, Sci_Position length, int } else if (ch == '\'' && chPrev == '=') { state = SCE_H_SINGLESTRING; } else { - if (IsNumber(styler.GetStartSegment(), styler)) { + if (IsNumberChar(styler[styler.GetStartSegment()])) { styler.ColourTo(i - 1, SCE_H_NUMBER); } else { styler.ColourTo(i - 1, StateToPrint); diff --git a/scintilla/lexilla/lexers/LexJulia.cxx b/scintilla/lexilla/lexers/LexJulia.cxx index 7bbbe51efa..a37e428a83 100644 --- a/scintilla/lexilla/lexers/LexJulia.cxx +++ b/scintilla/lexilla/lexers/LexJulia.cxx @@ -252,9 +252,10 @@ static int is_wc_cat_id_start(uint32_t wc) { wc == 0x223f || wc == 0x22be || wc == 0x22bf || // ∿, ⊾, ⊿ wc == 0x22a4 || wc == 0x22a5 || // ⊤ ⊥ - (wc >= 0x2202 && wc <= 0x2233 && + (wc >= 0x2200 && wc <= 0x2233 && (wc == 0x2202 || wc == 0x2205 || wc == 0x2206 || // ∂, ∅, ∆ wc == 0x2207 || wc == 0x220e || wc == 0x220f || // ∇, ∎, ∏ + wc == 0x2200 || wc == 0x2203 || wc == 0x2204 || // ∀, ∃, ∄ wc == 0x2210 || wc == 0x2211 || // ∐, ∑ wc == 0x221e || wc == 0x221f || // ∞, ∟ wc >= 0x222b)) || // ∫, ∬, ∭, ∮, ∯, ∰, ∱, ∲, ∳ @@ -1020,6 +1021,7 @@ void SCI_METHOD LexerJulia::Lex(Sci_PositionU startPos, Sci_Position length, int } } else if (sc.ch == '!') { sc.SetState(SCE_JULIA_OPERATOR); + transpose = false; } else if (sc.ch == '\'') { if (transpose) { sc.SetState(SCE_JULIA_OPERATOR); diff --git a/scintilla/lexilla/lexers/LexMarkdown.cxx b/scintilla/lexilla/lexers/LexMarkdown.cxx index 36346398bb..837cb89bfc 100644 --- a/scintilla/lexilla/lexers/LexMarkdown.cxx +++ b/scintilla/lexilla/lexers/LexMarkdown.cxx @@ -66,6 +66,7 @@ constexpr bool IsNewline(const int ch) { } // True if can follow ch down to the end with possibly trailing whitespace +// Does not set the state SCE_MARKDOWN_LINE_BEGIN as to allow further processing static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU endPos, StyleContext &sc) { Sci_Position i = 0; while (sc.GetRelative(++i) == ch) @@ -74,9 +75,8 @@ static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU e while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos) ++i; if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) { + sc.SetState(state); sc.Forward(i); - sc.ChangeState(state); - sc.SetState(SCE_MARKDOWN_LINE_BEGIN); return true; } else return false; @@ -126,15 +126,15 @@ static bool AtTermStart(StyleContext &sc) { static bool IsCompleteStyleRegion(StyleContext &sc, const char *token) { bool found = false; - const size_t start = strlen(token); + const size_t start = strlen(token); Sci_Position i = static_cast(start); while (!IsNewline(sc.GetRelative(i))) { // make sure an empty pair of single-char tokens doesn't match // with a longer token: {*}{*} != {**} - if (sc.GetRelative(i) == *token && sc.GetRelative(i - 1) != *token) { - found = start > 1U ? sc.GetRelative(i + 1) == token[1] : true; - break; - } + if (sc.GetRelative(i) == *token && sc.GetRelative(i - 1) != *token) { + found = start > 1U ? sc.GetRelative(i + 1) == token[1] : true; + break; + } i++; } return AtTermStart(sc) && found; @@ -160,14 +160,14 @@ static bool IsValidHrule(const Sci_PositionU endPos, StyleContext &sc) { } else { sc.SetState(SCE_MARKDOWN_DEFAULT); - return false; + return false; } } } } static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, - WordList **, Accessor &styler) { + WordList **, Accessor &styler) { Sci_PositionU endPos = startPos + length; int precharCount = 0; bool isLinkNameDetecting = false; @@ -176,6 +176,10 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int // in the default state. bool freezeCursor = false; + // property lexer.markdown.header.eolfill + // Set to 1 to highlight all ATX header text. + bool headerEOLFill = styler.GetPropertyInt("lexer.markdown.header.eolfill", 0) == 1; + StyleContext sc(startPos, static_cast(length), initStyle, styler); while (sc.More()) { @@ -265,22 +269,45 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int } else if (sc.state == SCE_MARKDOWN_LINE_BEGIN) { // Header - if (sc.Match("######")) - SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc); - else if (sc.Match("#####")) - SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc); - else if (sc.Match("####")) - SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); - else if (sc.Match("###")) - SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc); - else if (sc.Match("##")) - SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc); + if (sc.Match("######")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER6); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc); + } + else if (sc.Match("#####")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER5); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc); + } + else if (sc.Match("####")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER4); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); + } + else if (sc.Match("###")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER3); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc); + } + else if (sc.Match("##")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER2); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc); + } else if (sc.Match("#")) { // Catch the special case of an unordered list if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { precharCount = 0; sc.SetState(SCE_MARKDOWN_PRECHAR); } + else if (headerEOLFill) { + sc.SetState(SCE_MARKDOWN_HEADER1); + } else SetStateAndZoom(SCE_MARKDOWN_HEADER1, 1, '#', sc); } @@ -292,14 +319,18 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int sc.SetState(SCE_MARKDOWN_DEFAULT); } else if (sc.ch == '=') { - if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc)) - ; + if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc)) { + if (!headerEOLFill) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } else sc.SetState(SCE_MARKDOWN_DEFAULT); } else if (sc.ch == '-') { - if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc)) - ; + if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc)) { + if (!headerEOLFill) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } else { precharCount = 0; sc.SetState(SCE_MARKDOWN_PRECHAR); @@ -315,9 +346,15 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int // The header lasts until the newline else if (sc.state == SCE_MARKDOWN_HEADER1 || sc.state == SCE_MARKDOWN_HEADER2 || - sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 || - sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) { - if (IsNewline(sc.ch)) + sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 || + sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) { + if (headerEOLFill) { + if (sc.atLineStart) { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + freezeCursor = true; + } + } + else if (IsNewline(sc.ch)) sc.SetState(SCE_MARKDOWN_LINE_BEGIN); } @@ -367,21 +404,21 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int // Any link if (sc.state == SCE_MARKDOWN_LINK) { if (sc.Match("](") && sc.GetRelative(-1) != '\\') { - sc.Forward(2); - isLinkNameDetecting = true; + sc.Forward(2); + isLinkNameDetecting = true; } else if (sc.Match("]:") && sc.GetRelative(-1) != '\\') { - sc.Forward(2); - sc.SetState(SCE_MARKDOWN_DEFAULT); + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); } else if (!isLinkNameDetecting && sc.ch == ']' && sc.GetRelative(-1) != '\\') { - sc.Forward(); - sc.SetState(SCE_MARKDOWN_DEFAULT); + sc.Forward(); + sc.SetState(SCE_MARKDOWN_DEFAULT); } else if (isLinkNameDetecting && sc.ch == ')' && sc.GetRelative(-1) != '\\') { - sc.Forward(); - sc.SetState(SCE_MARKDOWN_DEFAULT); - isLinkNameDetecting = false; + sc.Forward(); + sc.SetState(SCE_MARKDOWN_DEFAULT); + isLinkNameDetecting = false; } } @@ -393,11 +430,11 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int } // Links and Images if (sc.Match("![")) { - sc.SetState(SCE_MARKDOWN_LINK); - sc.Forward(1); + sc.SetState(SCE_MARKDOWN_LINK); + sc.Forward(1); } else if (sc.ch == '[' && sc.GetRelative(-1) != '\\') { - sc.SetState(SCE_MARKDOWN_LINK); + sc.SetState(SCE_MARKDOWN_LINK); } // Code - also a special case for alternate inside spacing else if (sc.Match("``") && sc.GetRelative(3) != ' ' && AtTermStart(sc)) { @@ -412,7 +449,7 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int else if (sc.Match("**") && sc.GetRelative(2) != ' ' && IsCompleteStyleRegion(sc, "**")) { sc.SetState(SCE_MARKDOWN_STRONG1); sc.Forward(); - } + } else if (sc.Match("__") && sc.GetRelative(2) != ' ' && IsCompleteStyleRegion(sc, "__")) { sc.SetState(SCE_MARKDOWN_STRONG2); sc.Forward(); @@ -420,12 +457,13 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int // Emphasis else if (sc.ch == '*' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "*")) { sc.SetState(SCE_MARKDOWN_EM1); - } else if (sc.ch == '_' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "_")) { + } + else if (sc.ch == '_' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "_")) { sc.SetState(SCE_MARKDOWN_EM2); } // Strikeout else if (sc.Match("~~") && !(sc.GetRelative(2) == '~' || sc.GetRelative(2) == ' ') && - IsCompleteStyleRegion(sc, "~~")) { + IsCompleteStyleRegion(sc, "~~")) { sc.SetState(SCE_MARKDOWN_STRIKEOUT); sc.Forward(); } diff --git a/scintilla/lexilla/lexers/LexMatlab.cxx b/scintilla/lexilla/lexers/LexMatlab.cxx index e9b2409fd9..4a3ae85f82 100644 --- a/scintilla/lexilla/lexers/LexMatlab.cxx +++ b/scintilla/lexilla/lexers/LexMatlab.cxx @@ -22,6 +22,9 @@ ** ** Changes by John Donoghue 2017/01/18 ** - update matlab block comment detection + ** + ** Changes by Andrey Smolyakov 2022/04/15 + ** - add support for "arguments" block and class definition syntax **/ // Copyright 1998-2001 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. @@ -71,6 +74,12 @@ static int CheckKeywordFoldPoint(char *str) { strcmp ("try", str) == 0 || strcmp ("do", str) == 0 || strcmp ("parfor", str) == 0 || + strcmp ("classdef", str) == 0 || + strcmp ("spmd", str) == 0 || + strcmp ("arguments", str) == 0 || + strcmp ("methods", str) == 0 || + strcmp ("properties", str) == 0 || + strcmp ("events", str) == 0 || strcmp ("function", str) == 0) return 1; if (strncmp("end", str, 3) == 0 || @@ -89,6 +98,29 @@ static bool IsSpaceToEOL(Sci_Position startPos, Accessor &styler) { return true; } +#define MATLAB_STATE_FOLD_LVL_OFFSET 8 +#define MATLAB_STATE_FOLD_LVL_MASK (0xFF00) +#define MATLAB_STATE_FLAGS_OFFSET 16 +#define MATLAB_STATE_COMM_DEPTH_OFFSET 0 +#define MATLAB_STATE_COMM_DEPTH_MASK (0xFF) +#define MATLAB_STATE_EXPECTING_ARG_BLOCK (1 << MATLAB_STATE_FLAGS_OFFSET) +#define MATLAB_STATE_IN_CLASS_SCOPE (1 <<(MATLAB_STATE_FLAGS_OFFSET+1)) + +static int ComposeLineState(int commentDepth, + int foldingLevel, + int expectingArgumentsBlock, + int inClassScope) { + + return ((commentDepth << MATLAB_STATE_COMM_DEPTH_OFFSET) + & MATLAB_STATE_COMM_DEPTH_MASK) | + ((foldingLevel << MATLAB_STATE_FOLD_LVL_OFFSET) + & MATLAB_STATE_FOLD_LVL_MASK) | + (expectingArgumentsBlock + & MATLAB_STATE_EXPECTING_ARG_BLOCK) | + (inClassScope + & MATLAB_STATE_IN_CLASS_SCOPE); +} + static void ColouriseMatlabOctaveDoc( Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler, @@ -111,28 +143,68 @@ static void ColouriseMatlabOctaveDoc( // approximate column position of the current character in a line int column = 0; - // use the line state of each line to store the block comment depth + // This line contains a function declaration + bool funcDeclarationLine = false; + // We've just seen "function" keyword, so now we may expect the "arguments" + // keyword opening the corresponding code block + int expectingArgumentsBlock = 0; + // Current line's folding level + int foldingLevel = 0; + // Current line in in class scope + int inClassScope = 0; + + // use the line state of each line to store the block comment depth Sci_Position curLine = styler.GetLine(startPos); - int commentDepth = curLine > 0 ? styler.GetLineState(curLine-1) : 0; + int commentDepth = 0; + // Restore the previous line's state, if there was such a line + if (curLine > 0) { + int prevState = styler.GetLineState(curLine-1); + commentDepth = (prevState & MATLAB_STATE_COMM_DEPTH_MASK) + >> MATLAB_STATE_COMM_DEPTH_OFFSET; + foldingLevel = (prevState & MATLAB_STATE_FOLD_LVL_MASK) + >> MATLAB_STATE_FOLD_LVL_OFFSET; + expectingArgumentsBlock = prevState & MATLAB_STATE_EXPECTING_ARG_BLOCK; + inClassScope = prevState & MATLAB_STATE_IN_CLASS_SCOPE; + } StyleContext sc(startPos, length, initStyle, styler); for (; sc.More(); sc.Forward(), column++) { - if(sc.atLineStart) { + if(sc.atLineStart) { // set the line state to the current commentDepth curLine = styler.GetLine(sc.currentPos); - styler.SetLineState(curLine, commentDepth); + styler.SetLineState(curLine, ComposeLineState( + commentDepth, foldingLevel, expectingArgumentsBlock, inClassScope)); // reset the column to 0, nonSpace to -1 (not set) column = 0; nonSpaceColumn = -1; + + // Reset the flag + funcDeclarationLine = false; + } + + // Only comments allowed between the function declaration and the + // arguments code block + if (expectingArgumentsBlock && !funcDeclarationLine) { + if ((sc.state != SCE_MATLAB_KEYWORD) && + (sc.state != SCE_MATLAB_COMMENT) && + (sc.state != SCE_MATLAB_DEFAULT)) { + expectingArgumentsBlock = 0; + styler.SetLineState(curLine, ComposeLineState( + commentDepth, foldingLevel, expectingArgumentsBlock, inClassScope)); + } + } + + // We've just left the class scope + if ((foldingLevel ==0) && inClassScope) { + inClassScope = 0; } // save the column position of first non space character in a line - if((nonSpaceColumn == -1) && (! IsASpace(sc.ch))) - { + if((nonSpaceColumn == -1) && (! IsASpace(sc.ch))) { nonSpaceColumn = column; } @@ -145,10 +217,10 @@ static void ColouriseMatlabOctaveDoc( } else if (sc.ch == '\'') { sc.ForwardSetState(SCE_MATLAB_DEFAULT); transpose = true; - } else if(sc.ch == '.' && sc.chNext == '.') { - // we werent an operator, but a '...' - sc.ChangeState(SCE_MATLAB_COMMENT); - transpose = false; + } else if(sc.ch == '.' && sc.chNext == '.') { + // we werent an operator, but a '...' + sc.ChangeState(SCE_MATLAB_COMMENT); + transpose = false; } else { sc.SetState(SCE_MATLAB_DEFAULT); } @@ -159,19 +231,58 @@ static void ColouriseMatlabOctaveDoc( if (!isalnum(sc.ch) && sc.ch != '_') { char s[100]; sc.GetCurrent(s, sizeof(s)); + bool notKeyword = false; + transpose = false; if (keywords.InList(s)) { if (strcmp ("end", s) == 0 && allow_end_op) { sc.ChangeState(SCE_MATLAB_NUMBER); + notKeyword = true; + } else if (strcmp("function", s) == 0) { + // Need this flag to handle "arguments" block correctly + funcDeclarationLine = true; + expectingArgumentsBlock = ismatlab ? MATLAB_STATE_EXPECTING_ARG_BLOCK : 0; + } else if (strcmp("classdef", s) == 0) { + // Need this flag to process "events", "methods" and "properties" blocks + inClassScope = MATLAB_STATE_IN_CLASS_SCOPE; } - sc.SetState(SCE_MATLAB_DEFAULT); - transpose = false; + expectingArgumentsBlock = funcDeclarationLine ? expectingArgumentsBlock : 0; } else { - sc.ChangeState(SCE_MATLAB_IDENTIFIER); - sc.SetState(SCE_MATLAB_DEFAULT); - transpose = true; + // "arguments" is a keyword here, despite not being in the keywords list + if (expectingArgumentsBlock && (strcmp("arguments", s) == 0)) { + // No need to expect another arguments block + expectingArgumentsBlock = 0; + } else { + // Found an identifier or a keyword after the function declaration + // No need to wait for the arguments block anymore + expectingArgumentsBlock = funcDeclarationLine ? expectingArgumentsBlock : 0; + + // "properties", "methods" and "events" are not keywords if they're declared + // inside some function in methods block + // To avoid tracking possible nested functions scopes, lexer considers everything + // beyond level 2 of folding to be in a scope of some function declared in the + // methods block. It is ok for the valid syntax: classes can only be declared in + // a separate file, function - only in methods block. However, in case of the invalid + // syntax lexer may erroneously ignore a keyword. + if (!((inClassScope) && (foldingLevel <= 2) && ( + strcmp("properties", s) == 0 || + strcmp("methods", s) == 0 || + strcmp("events", s) == 0 ))) { + sc.ChangeState(SCE_MATLAB_IDENTIFIER); + transpose = true; + notKeyword = true; + } + } + } + + sc.SetState(SCE_MATLAB_DEFAULT); + if (!notKeyword) { + foldingLevel += CheckKeywordFoldPoint(s); } } + + styler.SetLineState(curLine, ComposeLineState( + commentDepth, foldingLevel, expectingArgumentsBlock, inClassScope)); } else if (sc.state == SCE_MATLAB_NUMBER) { if (!isdigit(sc.ch) && sc.ch != '.' && !(sc.ch == 'e' || sc.ch == 'E') @@ -182,10 +293,10 @@ static void ColouriseMatlabOctaveDoc( } else if (sc.state == SCE_MATLAB_STRING) { if (sc.ch == '\'') { if (sc.chNext == '\'') { - sc.Forward(); + sc.Forward(); } else { sc.ForwardSetState(SCE_MATLAB_DEFAULT); - } + } } } else if (sc.state == SCE_MATLAB_DOUBLEQUOTESTRING) { if (sc.ch == '\\' && !ismatlab) { @@ -203,27 +314,27 @@ static void ColouriseMatlabOctaveDoc( } else if (sc.state == SCE_MATLAB_COMMENT) { // end or start of a nested a block comment? if( IsCommentChar(sc.ch) && sc.chNext == '}' && nonSpaceColumn == column && IsSpaceToEOL(sc.currentPos+2, styler)) { - if(commentDepth > 0) commentDepth --; + if(commentDepth > 0) commentDepth --; curLine = styler.GetLine(sc.currentPos); - styler.SetLineState(curLine, commentDepth); + styler.SetLineState(curLine, ComposeLineState( + commentDepth, foldingLevel, expectingArgumentsBlock, inClassScope)); sc.Forward(); if (commentDepth == 0) { - sc.ForwardSetState(SCE_D_DEFAULT); + sc.ForwardSetState(SCE_MATLAB_DEFAULT); transpose = false; } - } - else if( IsCommentChar(sc.ch) && sc.chNext == '{' && nonSpaceColumn == column && IsSpaceToEOL(sc.currentPos+2, styler)) - { - commentDepth ++; + } else if( IsCommentChar(sc.ch) && sc.chNext == '{' && nonSpaceColumn == column && IsSpaceToEOL(sc.currentPos+2, styler)) { + commentDepth ++; curLine = styler.GetLine(sc.currentPos); - styler.SetLineState(curLine, commentDepth); + styler.SetLineState(curLine, ComposeLineState( + commentDepth, foldingLevel, expectingArgumentsBlock, inClassScope)); sc.Forward(); transpose = false; - } else if(commentDepth == 0) { + } else if(commentDepth == 0) { // single line comment if (sc.atLineEnd || sc.ch == '\r' || sc.ch == '\n') { sc.SetState(SCE_MATLAB_DEFAULT); @@ -242,7 +353,8 @@ static void ColouriseMatlabOctaveDoc( } } curLine = styler.GetLine(sc.currentPos); - styler.SetLineState(curLine, commentDepth); + styler.SetLineState(curLine, ComposeLineState( + commentDepth, foldingLevel, expectingArgumentsBlock, inClassScope)); sc.SetState(SCE_MATLAB_COMMENT); } else if (sc.ch == '!' && sc.chNext != '=' ) { if(ismatlab) { @@ -342,7 +454,7 @@ static void FoldMatlabOctaveDoc(Sci_PositionU startPos, Sci_Position length, int wordlen = 0; levelNext += CheckKeywordFoldPoint(word); - } + } } if (!IsASpace(ch)) visibleChars++; diff --git a/scintilla/lexilla/lexers/LexPowerShell.cxx b/scintilla/lexilla/lexers/LexPowerShell.cxx index 4e652787df..6c9e11ad85 100644 --- a/scintilla/lexilla/lexers/LexPowerShell.cxx +++ b/scintilla/lexilla/lexers/LexPowerShell.cxx @@ -29,19 +29,19 @@ using namespace Lexilla; // Extended to accept accented characters -static inline bool IsAWordChar(int ch) { +static inline bool IsAWordChar(int ch) noexcept { return ch >= 0x80 || isalnum(ch) || ch == '-' || ch == '_'; } static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler) { - WordList &keywords = *keywordlists[0]; - WordList &keywords2 = *keywordlists[1]; - WordList &keywords3 = *keywordlists[2]; - WordList &keywords4 = *keywordlists[3]; - WordList &keywords5 = *keywordlists[4]; - WordList &keywords6 = *keywordlists[5]; + const WordList &keywords = *keywordlists[0]; + const WordList &keywords2 = *keywordlists[1]; + const WordList &keywords3 = *keywordlists[2]; + const WordList &keywords4 = *keywordlists[3]; + const WordList &keywords5 = *keywordlists[4]; + const WordList &keywords6 = *keywordlists[5]; styler.StartAt(startPos); @@ -50,7 +50,7 @@ static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, for (; sc.More(); sc.Forward()) { if (sc.state == SCE_POWERSHELL_COMMENT) { - if (sc.atLineEnd) { + if (sc.MatchLineEnd()) { sc.SetState(SCE_POWERSHELL_DEFAULT); } } else if (sc.state == SCE_POWERSHELL_COMMENTSTREAM) { @@ -85,8 +85,6 @@ static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, // This is a single quote string if (sc.ch == '\'') { sc.ForwardSetState(SCE_POWERSHELL_DEFAULT); - } else if (sc.ch == '`') { - sc.Forward(); // skip next escaped character } } else if (sc.state == SCE_POWERSHELL_HERE_STRING) { // This is a doubles quotes here-string @@ -109,7 +107,7 @@ static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, sc.SetState(SCE_POWERSHELL_DEFAULT); } } else if (sc.state == SCE_POWERSHELL_OPERATOR) { - if (!isoperator(static_cast(sc.ch))) { + if (!isoperator(sc.ch)) { sc.SetState(SCE_POWERSHELL_DEFAULT); } } else if (sc.state == SCE_POWERSHELL_IDENTIFIER) { @@ -150,7 +148,7 @@ static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, sc.SetState(SCE_POWERSHELL_VARIABLE); } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { sc.SetState(SCE_POWERSHELL_NUMBER); - } else if (isoperator(static_cast(sc.ch))) { + } else if (isoperator(sc.ch)) { sc.SetState(SCE_POWERSHELL_OPERATOR); } else if (IsAWordChar(sc.ch)) { sc.SetState(SCE_POWERSHELL_IDENTIFIER); @@ -167,10 +165,10 @@ static void ColourisePowerShellDoc(Sci_PositionU startPos, Sci_Position length, // and to make it possible to fiddle the current level for "} else {". static void FoldPowerShellDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler) { - bool foldComment = styler.GetPropertyInt("fold.comment") != 0; - bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; - bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; - Sci_PositionU endPos = startPos + length; + const bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + const bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; + const Sci_PositionU endPos = startPos + length; int visibleChars = 0; Sci_Position lineCurrent = styler.GetLine(startPos); int levelCurrent = SC_FOLDLEVELBASE; @@ -182,12 +180,12 @@ static void FoldPowerShellDoc(Sci_PositionU startPos, Sci_Position length, int i int styleNext = styler.StyleAt(startPos); int style = initStyle; for (Sci_PositionU i = startPos; i < endPos; i++) { - char ch = chNext; + const char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); - int stylePrev = style; + const int stylePrev = style; style = styleNext; styleNext = styler.StyleAt(i + 1); - bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + const bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); if (style == SCE_POWERSHELL_OPERATOR) { if (ch == '{') { // Measure the minimum before a '{' to allow diff --git a/scintilla/lexilla/lexers/LexProps.cxx b/scintilla/lexilla/lexers/LexProps.cxx index 7dadb2c6e1..700a0ab510 100644 --- a/scintilla/lexilla/lexers/LexProps.cxx +++ b/scintilla/lexilla/lexers/LexProps.cxx @@ -119,7 +119,7 @@ static void FoldPropsDoc(Sci_PositionU startPos, Sci_Position length, int, WordL char chNext = styler[startPos]; int styleNext = styler.StyleAt(startPos); bool headerPoint = false; - int lev; + int levelPrevious = (lineCurrent > 0) ? styler.LevelAt(lineCurrent - 1) : SC_FOLDLEVELBASE; for (Sci_PositionU i = startPos; i < endPos; i++) { const char ch = chNext; @@ -134,27 +134,19 @@ static void FoldPropsDoc(Sci_PositionU startPos, Sci_Position length, int, WordL } if (atEOL) { - lev = SC_FOLDLEVELBASE; - - if (lineCurrent > 0) { - const int levelPrevious = styler.LevelAt(lineCurrent - 1); - + int lev = levelPrevious & SC_FOLDLEVELNUMBERMASK; + if (headerPoint) { + lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG; if (levelPrevious & SC_FOLDLEVELHEADERFLAG) { - lev = SC_FOLDLEVELBASE + 1; - } else { - lev = levelPrevious & SC_FOLDLEVELNUMBERMASK; + // previous section is empty + styler.SetLevel(lineCurrent - 1, SC_FOLDLEVELBASE); } + } else if (levelPrevious & SC_FOLDLEVELHEADERFLAG) { + lev += 1; } - if (headerPoint) { - lev = SC_FOLDLEVELBASE; - } if (visibleChars == 0 && foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; - - if (headerPoint) { - lev |= SC_FOLDLEVELHEADERFLAG; - } if (lev != styler.LevelAt(lineCurrent)) { styler.SetLevel(lineCurrent, lev); } @@ -162,23 +154,18 @@ static void FoldPropsDoc(Sci_PositionU startPos, Sci_Position length, int, WordL lineCurrent++; visibleChars = 0; headerPoint = false; + levelPrevious = lev; } if (!isspacechar(ch)) visibleChars++; } - if (lineCurrent > 0) { - const int levelPrevious = styler.LevelAt(lineCurrent - 1); - if (levelPrevious & SC_FOLDLEVELHEADERFLAG) { - lev = SC_FOLDLEVELBASE + 1; - } else { - lev = levelPrevious & SC_FOLDLEVELNUMBERMASK; - } - } else { - lev = SC_FOLDLEVELBASE; + int level = levelPrevious & SC_FOLDLEVELNUMBERMASK; + if (levelPrevious & SC_FOLDLEVELHEADERFLAG) { + level += 1; } int flagsNext = styler.LevelAt(lineCurrent); - styler.SetLevel(lineCurrent, lev | (flagsNext & ~SC_FOLDLEVELNUMBERMASK)); + styler.SetLevel(lineCurrent, level | (flagsNext & ~SC_FOLDLEVELNUMBERMASK)); } static const char *const emptyWordListDesc[] = { diff --git a/scintilla/lexilla/lexers/LexPython.cxx b/scintilla/lexilla/lexers/LexPython.cxx index 53d245da45..e9d1741e5d 100644 --- a/scintilla/lexilla/lexers/LexPython.cxx +++ b/scintilla/lexilla/lexers/LexPython.cxx @@ -231,6 +231,8 @@ struct OptionsPython { bool foldQuotes; bool foldCompact; bool unicodeIdentifiers; + int identifierAttributes; + int decoratorAttributes; OptionsPython() noexcept { whingeLevel = 0; @@ -244,6 +246,8 @@ struct OptionsPython { foldQuotes = false; foldCompact = false; unicodeIdentifiers = true; + identifierAttributes = 0; + decoratorAttributes = 0; } literalsAllowed AllowedLiterals() const noexcept { @@ -302,6 +306,12 @@ struct OptionSetPython : public OptionSet { DefineProperty("lexer.python.unicode.identifiers", &OptionsPython::unicodeIdentifiers, "Set to 0 to not recognise Python 3 Unicode identifiers."); + DefineProperty("lexer.python.identifier.attributes", &OptionsPython::identifierAttributes, + "Set to 1 to recognise Python identifier attributes."); + + DefineProperty("lexer.python.decorator.attributes", &OptionsPython::decoratorAttributes, + "Set to 1 to recognise Python decorator attributes."); + DefineWordListSets(pythonWordListDesc); } }; @@ -330,6 +340,7 @@ LexicalClass lexicalClasses[] = { 17, "SCE_P_FCHARACTER", "literal string interpolated", "Single quoted f-string", 18, "SCE_P_FTRIPLE", "literal string interpolated", "Triple quoted f-string", 19, "SCE_P_FTRIPLEDOUBLE", "literal string interpolated", "Triple double quoted f-string", + 20, "SCE_P_ATTRIBUTE", "identifier", "Attribute of identifier", }; } @@ -641,6 +652,43 @@ void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, in if (subStyle >= 0) { style = subStyle; } + if (options.identifierAttributes > 0 || options.decoratorAttributes > 0) { + // Does the user even want attributes styled? + Sci_Position pos = styler.GetStartSegment() - 1; + unsigned char ch = styler.SafeGetCharAt(pos, '\0'); + while (ch != '\0' && (ch == '.' || ch == ' ' || ch == '\\' || ch == '\t' || ch == '\n' || ch == '\r')) { + // Backwards search for a . while only allowing certain valid characters + if (IsAWordChar(ch, options.unicodeIdentifiers)) { + break; + } + pos--; + ch = styler.SafeGetCharAt(pos, '\0'); + } + if (pos < 0 || ch == '.') { + // Is this an attribute we could style? if it is, do as asked + bool isComment = false; + bool isDecoratorAttribute = false; + const Sci_Position attrLine = styler.GetLine(pos); + for (Sci_Position i = styler.LineStart(attrLine); i < pos; i++) { + const char attrCh = styler[i]; + if (attrCh == '@') + isDecoratorAttribute = true; + if (attrCh == '#') + isComment = true; + // Detect if this attribute belongs to a decorator + if (!(ch == ' ' || ch == '\t')) + break; + } + if (((isDecoratorAttribute) && (!isComment)) && (((options.decoratorAttributes == 1) && (style == SCE_P_IDENTIFIER)) || (options.decoratorAttributes == 2))){ + // Style decorator attributes as decorators but respect already styled identifiers (unless requested to ignore already styled identifiers) + style = SCE_P_DECORATOR; + } + if (((!isDecoratorAttribute) && (!isComment)) && (((options.identifierAttributes == 1) && (style == SCE_P_IDENTIFIER)) || (options.identifierAttributes == 2))){ + // Style attributes and ignore decorator attributes but respect already styled identifiers (unless requested to ignore already styled identifiers) + style = SCE_P_ATTRIBUTE; + } + } + } } sc.ChangeState(style); sc.SetState(SCE_P_DEFAULT); diff --git a/scintilla/lexilla/lexers/LexR.cxx b/scintilla/lexilla/lexers/LexR.cxx index 2a1da0cb62..9f9824e907 100644 --- a/scintilla/lexilla/lexers/LexR.cxx +++ b/scintilla/lexilla/lexers/LexR.cxx @@ -6,12 +6,8 @@ // Copyright 1998-2002 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. -#include -#include -#include -#include -#include -#include +#include +#include #include #include @@ -29,17 +25,17 @@ using namespace Lexilla; -static inline bool IsAWordChar(const int ch) { +namespace { + +inline bool IsAWordChar(int ch) noexcept { return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_'); } -static inline bool IsAWordStart(const int ch) { +inline bool IsAWordStart(int ch) noexcept { return (ch < 0x80) && (isalnum(ch) || ch == '_'); } -static inline bool IsAnOperator(const int ch) { - if (IsASCII(ch) && isalnum(ch)) - return false; +constexpr bool IsAnOperator(int ch) noexcept { // '.' left out as it is used to make up numbers if (ch == '-' || ch == '+' || ch == '!' || ch == '~' || ch == '?' || ch == ':' || ch == '*' || ch == '/' || @@ -51,36 +47,34 @@ static inline bool IsAnOperator(const int ch) { return false; } -static void ColouriseRDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], +void ColouriseRDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler) { - WordList &keywords = *keywordlists[0]; - WordList &keywords2 = *keywordlists[1]; - WordList &keywords3 = *keywordlists[2]; - + const WordList &keywords = *keywordlists[0]; + const WordList &keywords2 = *keywordlists[1]; + const WordList &keywords3 = *keywordlists[2]; // Do not leak onto next line - if (initStyle == SCE_R_INFIXEOL) + if (initStyle == SCE_R_INFIXEOL) { initStyle = SCE_R_DEFAULT; - + } StyleContext sc(startPos, length, initStyle, styler); for (; sc.More(); sc.Forward()) { - - if (sc.atLineStart && (sc.state == SCE_R_STRING)) { - // Prevent SCE_R_STRINGEOL from leaking back to previous line - sc.SetState(SCE_R_STRING); - } - // Determine if the current state should terminate. - if (sc.state == SCE_R_OPERATOR) { + switch (sc.state) { + case SCE_R_OPERATOR: sc.SetState(SCE_R_DEFAULT); - } else if (sc.state == SCE_R_NUMBER) { + break; + + case SCE_R_NUMBER: if (!IsADigit(sc.ch) && !(sc.ch == '.' && IsADigit(sc.chNext))) { sc.SetState(SCE_R_DEFAULT); } - } else if (sc.state == SCE_R_IDENTIFIER) { + break; + + case SCE_R_IDENTIFIER: if (!IsAWordChar(sc.ch)) { char s[100]; sc.GetCurrent(s, sizeof(s)); @@ -93,33 +87,33 @@ static void ColouriseRDoc(Sci_PositionU startPos, Sci_Position length, int initS } sc.SetState(SCE_R_DEFAULT); } - } else if (sc.state == SCE_R_COMMENT) { - if (sc.ch == '\r' || sc.ch == '\n') { + break; + + case SCE_R_COMMENT: + if (sc.MatchLineEnd()) { sc.SetState(SCE_R_DEFAULT); } - } else if (sc.state == SCE_R_STRING) { + break; + + case SCE_R_STRING: + case SCE_R_STRING2: if (sc.ch == '\\') { if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { sc.Forward(); } - } else if (sc.ch == '\"') { + } else if ((sc.state == SCE_R_STRING && sc.ch == '\"') || (sc.state == SCE_R_STRING2 && sc.ch == '\'')) { sc.ForwardSetState(SCE_R_DEFAULT); } - } else if (sc.state == SCE_R_INFIX) { + break; + + case SCE_R_INFIX: if (sc.ch == '%') { sc.ForwardSetState(SCE_R_DEFAULT); } else if (sc.atLineEnd) { sc.ChangeState(SCE_R_INFIXEOL); sc.ForwardSetState(SCE_R_DEFAULT); } - }else if (sc.state == SCE_R_STRING2) { - if (sc.ch == '\\') { - if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { - sc.Forward(); - } - } else if (sc.ch == '\'') { - sc.ForwardSetState(SCE_R_DEFAULT); - } + break; } // Determine if a new state should be entered. @@ -129,7 +123,7 @@ static void ColouriseRDoc(Sci_PositionU startPos, Sci_Position length, int initS } else if (IsAWordStart(sc.ch) ) { sc.SetState(SCE_R_IDENTIFIER); } else if (sc.Match('#')) { - sc.SetState(SCE_R_COMMENT); + sc.SetState(SCE_R_COMMENT); } else if (sc.ch == '\"') { sc.SetState(SCE_R_STRING); } else if (sc.ch == '%') { @@ -147,11 +141,11 @@ static void ColouriseRDoc(Sci_PositionU startPos, Sci_Position length, int initS // Store both the current line's fold level and the next lines in the // level store to make it easy to pick up with each increment // and to make it possible to fiddle the current level for "} else {". -static void FoldRDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], +void FoldRDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) { - bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; - bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; - Sci_PositionU endPos = startPos + length; + const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + const bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; + const Sci_PositionU endPos = startPos + length; int visibleChars = 0; Sci_Position lineCurrent = styler.GetLine(startPos); int levelCurrent = SC_FOLDLEVELBASE; @@ -162,11 +156,11 @@ static void FoldRDoc(Sci_PositionU startPos, Sci_Position length, int, WordList char chNext = styler[startPos]; int styleNext = styler.StyleAt(startPos); for (Sci_PositionU i = startPos; i < endPos; i++) { - char ch = chNext; + const char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); - int style = styleNext; + const int style = styleNext; styleNext = styler.StyleAt(i + 1); - bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + const bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); if (style == SCE_R_OPERATOR) { if (ch == '{') { // Measure the minimum before a '{' to allow @@ -203,15 +197,15 @@ static void FoldRDoc(Sci_PositionU startPos, Sci_Position length, int, WordList } -static const char * const RWordLists[] = { - "Language Keywords", - "Base / Default package function", - "Other Package Functions", - "Unused", - "Unused", - 0, - }; - +const char * const RWordLists[] = { + "Language Keywords", + "Base / Default package function", + "Other Package Functions", + "Unused", + "Unused", + nullptr, +}; +} LexerModule lmR(SCLEX_R, ColouriseRDoc, "r", FoldRDoc, RWordLists); diff --git a/scintilla/lexilla/lexers/LexRuby.cxx b/scintilla/lexilla/lexers/LexRuby.cxx index 566d0cc7f8..af5857561d 100644 --- a/scintilla/lexilla/lexers/LexRuby.cxx +++ b/scintilla/lexilla/lexers/LexRuby.cxx @@ -64,6 +64,15 @@ static bool inline iswhitespace(char ch) { return ch == ' ' || ch == '\t'; } +static inline bool isQestionMarkChar(char chNext, char chNext2) { + // followed by a single character or escape sequence that corresponds to a single codepoint + if (isSafeAlnum(chNext)) { + return !isSafeWordcharOrHigh(chNext2); + } + // multibyte character, escape sequence, punctuation + return !IsASpace(chNext); +} + #define MAX_KEYWORD_LENGTH 200 #define STYLE_MASK 63 @@ -102,7 +111,7 @@ static bool keywordIsModifier(const char *word, Sci_Position pos, Accessor &styler); -static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, WordList &keywords, Accessor &styler, char *prevWord) { +static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, char ch, WordList &keywords, Accessor &styler, char *prevWord) { char s[MAX_KEYWORD_LENGTH]; Sci_PositionU i, j; Sci_PositionU lim = end - start + 1; // num chars to copy @@ -113,13 +122,22 @@ static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, WordList &keyw s[j] = styler[i]; } s[j] = '\0'; - int chAttr; + int chAttr = SCE_RB_IDENTIFIER; + int style = SCE_RB_DEFAULT; if (0 == strcmp(prevWord, "class")) chAttr = SCE_RB_CLASSNAME; else if (0 == strcmp(prevWord, "module")) chAttr = SCE_RB_MODULE_NAME; - else if (0 == strcmp(prevWord, "def")) + else if (0 == strcmp(prevWord, "def")) { chAttr = SCE_RB_DEFNAME; + if (ch == '.') { + if (strcmp(s, "self") == 0) { + style = SCE_RB_WORD_DEMOTED; + } else { + style = SCE_RB_IDENTIFIER; + } + } + } else if (keywords.InList(s) && ((start == 0) || !followsDot(start - 1, styler))) { if (keywordIsAmbiguous(s) && keywordIsModifier(s, start, styler)) { @@ -136,15 +154,15 @@ static int ClassifyWordRb(Sci_PositionU start, Sci_PositionU end, WordList &keyw chAttr = SCE_RB_WORD_DEMOTED; } else { chAttr = SCE_RB_WORD; + style = SCE_RB_WORD; + strcpy(prevWord, s); } - } else - chAttr = SCE_RB_IDENTIFIER; - styler.ColourTo(end, chAttr); - if (chAttr == SCE_RB_WORD) { - strcpy(prevWord, s); - } else { + } + if (style == SCE_RB_DEFAULT) { + style = chAttr; prevWord[0] = 0; } + styler.ColourTo(end, style); return chAttr; } @@ -461,7 +479,6 @@ static Sci_Position findExpressionStart(Sci_Position pos, static bool sureThisIsNotHeredoc(Sci_Position lt2StartPos, Accessor &styler) { - int prevStyle; // Use full document, not just part we're styling Sci_Position lengthDoc = styler.Length(); Sci_Position lineStart = styler.GetLine(lt2StartPos); @@ -478,9 +495,10 @@ static bool sureThisIsNotHeredoc(Sci_Position lt2StartPos, if (firstWordPosn >= lt2StartPos) { return definitely_not_a_here_doc; } - prevStyle = styler.StyleAt(firstWordPosn); + int prevStyle = styler.StyleAt(firstWordPosn); // If we have '<<' following a keyword, it's not a heredoc if (prevStyle != SCE_RB_IDENTIFIER + && prevStyle != SCE_RB_GLOBAL // $stdout and $stderr && prevStyle != SCE_RB_SYMBOL && prevStyle != SCE_RB_INSTANCE_VAR && prevStyle != SCE_RB_CLASS_VAR) { @@ -595,7 +613,7 @@ static bool sureThisIsNotHeredoc(Sci_Position lt2StartPos, return definitely_not_a_here_doc; } else { char ch = styler[j]; - if (ch == '#' || isEOLChar(ch)) { + if (ch == '#' || isEOLChar(ch) || ch == '.' || ch == ',') { // This is OK, so break and continue; break; } else { @@ -714,6 +732,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init false); bool preferRE = true; + bool afterDef = false; int state = initStyle; Sci_Position lengthDoc = startPos + length; @@ -791,7 +810,12 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init if (HereDoc.State == 1 && isEOLChar(ch)) { // Begin of here-doc (the line after the here-doc delimiter): HereDoc.State = 2; - styler.ColourTo(i-1, state); + if (state == SCE_RB_WORD) { + const Sci_Position wordStartPos = styler.GetStartSegment(); + ClassifyWordRb(wordStartPos, i - 1, ch, keywords, styler, prevWord); + } else { + styler.ColourTo(i - 1, state); + } // Don't check for a missing quote, just jump into // the here-doc state state = SCE_RB_HERE_Q; @@ -862,7 +886,10 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init Quote.New(); Quote.Open(ch); } else if (ch == '<' && chNext == '<' && chNext2 != '=') { - + if (afterDef) { + afterDef = false; + prevWord[0] = 0; + } // Recognise the '<<' symbol - either a here document or a binary op styler.ColourTo(i - 1, state); i++; @@ -893,6 +920,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init } preferRE = (state != SCE_RB_HERE_DELIM); } else if (ch == ':') { + afterDef = false; styler.ColourTo(i - 1, state); if (chNext == ':') { // Mark "::" as an operator, not symbol start @@ -999,7 +1027,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init styler.ColourTo(i, SCE_RB_SYMBOL); state = SCE_RB_DEFAULT; } - } else if (!preferRE) { + } else if (!preferRE && !IsASpace(chNext)) { // Don't color symbol strings (yet) // Just color the ":" and color rest as string styler.ColourTo(i, SCE_RB_SYMBOL); @@ -1009,7 +1037,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init state = SCE_RB_DEFAULT; preferRE = true; } - } else if (ch == '%') { + } else if (ch == '%' && !afterDef) { styler.ColourTo(i - 1, state); bool have_string = false; if (strchr(q_chars, chNext) && !isSafeWordcharOrHigh(chNext2)) { @@ -1046,9 +1074,24 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init preferRE = true; } } else if (ch == '?') { + afterDef = false; styler.ColourTo(i - 1, state); - if (iswhitespace(chNext) || chNext == '\n' || chNext == '\r') { + if (isHighBitChar(chNext)) { + preferRE = false; + Sci_Position width = 1; + styler.MultiByteAccess()->GetCharacterAndWidth(i + 1, &width); + chNext = styler.SafeGetCharAt(i + 1 + width); + if (isSafeWordcharOrHigh(chNext)) { + styler.ColourTo(i, SCE_RB_OPERATOR); + i += width; + state = SCE_RB_WORD; + } else { + i += width; + styler.ColourTo(i, SCE_RB_NUMBER); + } + } else if (!isQestionMarkChar(chNext, chNext2)) { styler.ColourTo(i, SCE_RB_OPERATOR); + preferRE = chNext <= ' '; } else { // It's the start of a character code escape sequence // Color it as a number. @@ -1057,6 +1100,16 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init } } else if (isoperator(ch) || ch == '.') { styler.ColourTo(i - 1, state); + if (afterDef && ch != '.') { + afterDef = false; + prevWord[0] = 0; + if (chNext == '@' && (ch == '+' || ch == '-' || ch == '!')) { + // unary operator method + ch = chNext; + chNext = chNext2; + i += 1; + } + } styler.ColourTo(i, SCE_RB_OPERATOR); // If we're ending an expression or block, // assume it ends an object, and the ambivalent @@ -1082,6 +1135,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init } // Stay in default state } else if (isEOLChar(ch)) { + afterDef = false; // Make sure it's a true line-end, with no backslash if ((ch == '\r' || (ch == '\n' && chPrev != '\r')) && chPrev != '\\') { @@ -1089,6 +1143,9 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init preferRE = true; } } + if (afterDef && state != SCE_RB_DEFAULT) { + afterDef = false; + } } else if (state == SCE_RB_WORD) { if (ch == '.' || !isSafeWordcharOrHigh(ch)) { // Words include x? in all contexts, @@ -1127,9 +1184,10 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init preferRE = false; } else { Sci_Position wordStartPos = styler.GetStartSegment(); - int word_style = ClassifyWordRb(wordStartPos, i - 1, keywords, styler, prevWord); + int word_style = ClassifyWordRb(wordStartPos, i - 1, ch, keywords, styler, prevWord); switch (word_style) { case SCE_RB_WORD: + afterDef = strcmp(prevWord, "def") == 0; preferRE = RE_CanFollowKeyword(prevWord); break; @@ -1152,6 +1210,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init if (ch == '.') { // We might be redefining an operator-method preferRE = false; + afterDef = word_style == SCE_RB_DEFNAME; } // And if it's the first redo_char(i, ch, chNext, chNext2, state); // pass by ref @@ -1159,7 +1218,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init } } else if (state == SCE_RB_NUMBER) { if (!is_real_number) { - if (ch != '\\') { + if (ch != '\\' || chPrev == '\\') { styler.ColourTo(i, state); state = SCE_RB_DEFAULT; preferRE = false; @@ -1300,7 +1359,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init i - HereDoc.DelimiterLength + 1, lengthDoc, HereDoc.Delimiter)) { - styler.ColourTo(i - 1 - HereDoc.DelimiterLength, state); + styler.ColourTo(i - HereDoc.DelimiterLength, state); styler.ColourTo(i, SCE_RB_HERE_DELIM); state = SCE_RB_DEFAULT; preferRE = false; @@ -1459,7 +1518,7 @@ static void ColouriseRbDoc(Sci_PositionU startPos, Sci_Position length, int init if (state == SCE_RB_WORD) { // We've ended on a word, possibly at EOF, and need to // classify it. - (void) ClassifyWordRb(styler.GetStartSegment(), lengthDoc - 1, keywords, styler, prevWord); + (void) ClassifyWordRb(styler.GetStartSegment(), lengthDoc - 1, '\0', keywords, styler, prevWord); } else { styler.ColourTo(lengthDoc - 1, state); } @@ -1754,10 +1813,21 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle & SC_FOLDLEVELNUMBERMASK & ~SC_FOLDLEVELBASE); int levelCurrent = levelPrev; + char chPrev = '\0'; char chNext = styler[startPos]; int styleNext = styler.StyleAt(startPos); int stylePrev = startPos <= 1 ? SCE_RB_DEFAULT : styler.StyleAt(startPos - 1); - bool buffer_ends_with_eol = false; + // detect endless method definition to fix up code folding + enum class MethodDefinition { + None, + Define, + Operator, + Name, + Argument, + }; + MethodDefinition method_definition = MethodDefinition::None; + int argument_paren_count = 0; + for (Sci_PositionU i = startPos; i < endPos; i++) { char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); @@ -1800,8 +1870,10 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle // Don't decrement below 0 if (levelCurrent > 0) levelCurrent--; + } else if (!strcmp(prevWord, "def")) { + levelCurrent++; + method_definition = MethodDefinition::Define; } else if (!strcmp(prevWord, "if") - || !strcmp(prevWord, "def") || !strcmp(prevWord, "class") || !strcmp(prevWord, "module") || !strcmp(prevWord, "begin") @@ -1820,8 +1892,64 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle } else if (styleNext == SCE_RB_DEFAULT) { levelCurrent--; } + } else if (style == SCE_RB_STRING_QW) { + if (stylePrev != style) { + levelCurrent++; + } else if (styleNext != style) { + levelCurrent--; + } + } + if (method_definition != MethodDefinition::None) { + switch (method_definition) { + case MethodDefinition::Define: + if (style == SCE_RB_OPERATOR) { + method_definition = MethodDefinition::Operator; + } else if (style == SCE_RB_DEFNAME || style == SCE_RB_WORD_DEMOTED || style == SCE_RB_CLASSNAME || style == SCE_RB_IDENTIFIER) { + method_definition = MethodDefinition::Name; + } else if (!(style == SCE_RB_WORD || IsASpaceOrTab(ch))) { + method_definition = MethodDefinition::None; + } + if (method_definition <= MethodDefinition::Define) { + break; + } + // fall through for unary operator or single letter name + [[fallthrough]]; + case MethodDefinition::Operator: + case MethodDefinition::Name: + if (isEOLChar(chNext) || chNext == '#') { + method_definition = MethodDefinition::None; + } else if (chNext == '(' || chNext <= ' ') { + // setter method cannot be defined in an endless method definition. + if (ch == '=' && (method_definition == MethodDefinition::Name || chPrev == ']')) { + method_definition = MethodDefinition::None; + } else { + method_definition = MethodDefinition::Argument; + argument_paren_count = 0; + } + } + break; + case MethodDefinition::Argument: + if (style == SCE_RB_OPERATOR) { + if (ch == '(') { + ++argument_paren_count; + } else if (ch == ')') { + --argument_paren_count; + } else if (argument_paren_count == 0) { + method_definition = MethodDefinition::None; + if (ch == '=' && levelCurrent > 0) { + levelCurrent--; + } + } + } else if (argument_paren_count == 0 && !IsASpaceOrTab(ch)) { + // '=' must be first character after method name or right parenthesis + method_definition = MethodDefinition::None; + } + break; + default: + break; + } } - if (atEOL) { + if (atEOL || (i == endPos - 1)) { int lev = levelPrev; if (visibleChars == 0 && foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; @@ -1831,23 +1959,14 @@ static void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle lineCurrent++; levelPrev = levelCurrent; visibleChars = 0; - buffer_ends_with_eol = true; + method_definition = MethodDefinition::None; + argument_paren_count = 0; } else if (!isspacechar(ch)) { visibleChars++; - buffer_ends_with_eol = false; } + chPrev = ch; stylePrev = style; } - // Fill in the real level of the next line, keeping the current flags as they will be filled in later - if (!buffer_ends_with_eol) { - int new_lev = levelCurrent; - if (visibleChars == 0 && foldCompact) - new_lev |= SC_FOLDLEVELWHITEFLAG; - if ((levelCurrent > levelPrev) && (visibleChars > 0)) - new_lev |= SC_FOLDLEVELHEADERFLAG; - levelCurrent = new_lev; - } - styler.SetLevel(lineCurrent, levelCurrent|SC_FOLDLEVELBASE); } static const char *const rubyWordListDesc[] = { diff --git a/scintilla/lexilla/lexers/LexVHDL.cxx b/scintilla/lexilla/lexers/LexVHDL.cxx index b64eb9ebd0..96ec4a548d 100644 --- a/scintilla/lexilla/lexers/LexVHDL.cxx +++ b/scintilla/lexilla/lexers/LexVHDL.cxx @@ -235,7 +235,7 @@ static void FoldNoBoxVHDLDoc( // Decided it would be smarter to have the lexer have all keywords included. Therefore I // don't check if the style for the keywords that I use to adjust the levels. char words[] = - "architecture begin block case component else elsif end entity generate loop package process record then " + "architecture begin block case component else elsif end entity for generate loop package process record then " "procedure protected function when units"; WordList keywords; keywords.Set(words); @@ -396,7 +396,6 @@ static void FoldNoBoxVHDLDoc( if ( strcmp(s, "architecture") == 0 || strcmp(s, "case") == 0 || - strcmp(s, "generate") == 0 || strcmp(s, "block") == 0 || strcmp(s, "loop") == 0 || strcmp(s, "package") ==0 || @@ -413,6 +412,16 @@ static void FoldNoBoxVHDLDoc( } levelNext++; } + } else if (strcmp(s, "generate") == 0){ + if (strcmp(prevWord, "end") != 0 && + strcmp(prevWord, "else") != 0 && // vhdl08 else generate + strcmp(prevWord, "case") != 0) // vhdl08 case generate + { + if (levelMinCurrentElse > levelNext) { + levelMinCurrentElse = levelNext; + } + levelNext++; + } } else if ( strcmp(s, "component") == 0 || strcmp(s, "entity") == 0 || @@ -479,7 +488,7 @@ static void FoldNoBoxVHDLDoc( } else if (strcmp(s, "end") == 0) { levelNext--; - } else if(strcmp(s, "elsif") == 0) { // elsif is followed by then so folding occurs correctly + } else if(strcmp(s, "elsif") == 0) { // elsif is followed by then or generate so folding occurs correctly levelNext--; } else if (strcmp(s, "else") == 0) { if(strcmp(prevWord, "when") != 0) // ignore a <= x when y else z; @@ -489,7 +498,8 @@ static void FoldNoBoxVHDLDoc( } else if( ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "architecture") == 0)) || ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "function") == 0)) || - ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "procedure") == 0))) + ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "procedure") == 0)) || + ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "generate") == 0))) { levelMinCurrentBegin = levelNext - 1; } diff --git a/scintilla/lexilla/lexlib/LexAccessor.cxx b/scintilla/lexilla/lexlib/LexAccessor.cxx index 7a15fb6145..f0d6bdf82e 100644 --- a/scintilla/lexilla/lexlib/LexAccessor.cxx +++ b/scintilla/lexilla/lexlib/LexAccessor.cxx @@ -20,6 +20,7 @@ using namespace Lexilla; namespace Lexilla { bool LexAccessor::MatchIgnoreCase(Sci_Position pos, const char *s) { + assert(s); for (; *s; s++, pos++) { if (*s != MakeLowerCase(SafeGetCharAt(pos))) { return false; @@ -29,7 +30,8 @@ bool LexAccessor::MatchIgnoreCase(Sci_Position pos, const char *s) { } void LexAccessor::GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_, char *s, Sci_PositionU len) { - assert(startPos_ <= endPos_ && len != 0 && s != nullptr); + assert(s); + assert(startPos_ <= endPos_ && len != 0); endPos_ = std::min(endPos_, startPos_ + len - 1); len = endPos_ - startPos_; if (startPos_ >= static_cast(startPos) && endPos_ <= static_cast(endPos)) { @@ -42,6 +44,7 @@ void LexAccessor::GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_, char } void LexAccessor::GetRangeLowered(Sci_PositionU startPos_, Sci_PositionU endPos_, char *s, Sci_PositionU len) { + assert(s); GetRange(startPos_, endPos_, s, len); while (*s) { if (*s >= 'A' && *s <= 'Z') { @@ -55,7 +58,7 @@ std::string LexAccessor::GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_ assert(startPos_ < endPos_); const Sci_PositionU len = endPos_ - startPos_; std::string s(len, '\0'); - GetRange(startPos_, endPos_, s.data(), len); + GetRange(startPos_, endPos_, s.data(), len + 1); return s; } @@ -63,7 +66,7 @@ std::string LexAccessor::GetRangeLowered(Sci_PositionU startPos_, Sci_PositionU assert(startPos_ < endPos_); const Sci_PositionU len = endPos_ - startPos_; std::string s(len, '\0'); - GetRangeLowered(startPos_, endPos_, s.data(), len); + GetRangeLowered(startPos_, endPos_, s.data(), len + 1); return s; } diff --git a/scintilla/lexilla/lexlib/LexAccessor.h b/scintilla/lexilla/lexlib/LexAccessor.h index 8f76ed2a4c..62f1cb89bd 100644 --- a/scintilla/lexilla/lexlib/LexAccessor.h +++ b/scintilla/lexilla/lexlib/LexAccessor.h @@ -69,6 +69,9 @@ class LexAccessor { case 950: case 1361: encodingType = EncodingType::dbcs; + break; + default: + break; } } char operator[](Sci_Position position) { @@ -101,6 +104,7 @@ class LexAccessor { return encodingType; } bool Match(Sci_Position pos, const char *s) { + assert(s); for (int i=0; *s; i++) { if (*s != SafeGetCharAt(pos+i)) return false; @@ -120,6 +124,18 @@ class LexAccessor { char StyleAt(Sci_Position position) const { return pAccess->StyleAt(position); } + int StyleIndexAt(Sci_Position position) const { + return static_cast(pAccess->StyleAt(position)); + } + // Return style value from buffer when in buffer, else retrieve from document. + // This is faster and can avoid calls to Flush() as that may be expensive. + int BufferStyleAt(Sci_Position position) const { + const Sci_Position index = position - startPosStyling; + if (index >= 0 && index < validLen) { + return static_cast(styleBuf[index]); + } + return static_cast(pAccess->StyleAt(position)); + } Sci_Position GetLine(Sci_Position position) const { return pAccess->LineFromPosition(position); } diff --git a/scintilla/lexilla/lexlib/OptionSet.h b/scintilla/lexilla/lexlib/OptionSet.h index 1c5c2ec00c..e6f793b3b4 100644 --- a/scintilla/lexilla/lexlib/OptionSet.h +++ b/scintilla/lexilla/lexlib/OptionSet.h @@ -65,6 +65,8 @@ class OptionSet { } break; } + default: + break; } return false; } diff --git a/scintilla/lexilla/lexlib/PropSetSimple.cxx b/scintilla/lexilla/lexlib/PropSetSimple.cxx index 903d9790b6..b6c4166f4f 100644 --- a/scintilla/lexilla/lexlib/PropSetSimple.cxx +++ b/scintilla/lexilla/lexlib/PropSetSimple.cxx @@ -8,6 +8,7 @@ // Maintain a dictionary of properties #include +#include #include #include @@ -68,6 +69,7 @@ const char *PropSetSimple::Get(std::string_view key) const { int PropSetSimple::GetInt(std::string_view key, int defaultValue) const { const char *val = Get(key); + assert(val); if (*val) { return atoi(val); } diff --git a/scintilla/lexilla/lexlib/StyleContext.cxx b/scintilla/lexilla/lexlib/StyleContext.cxx index e7b5719312..b9f97488fe 100644 --- a/scintilla/lexilla/lexlib/StyleContext.cxx +++ b/scintilla/lexilla/lexlib/StyleContext.cxx @@ -6,6 +6,7 @@ // This file is in the public domain. #include +#include #include #include @@ -20,6 +21,33 @@ using namespace Lexilla; +StyleContext::StyleContext(Sci_PositionU startPos, Sci_PositionU length, + int initStyle, LexAccessor &styler_, char chMask) : + styler(styler_), + multiByteAccess((styler.Encoding() == EncodingType::eightBit) ? nullptr : styler.MultiByteAccess()), + lengthDocument(static_cast(styler.Length())), + endPos(((startPos + length) < lengthDocument) ? (startPos + length) : (lengthDocument+1)), + lineDocEnd(styler.GetLine(lengthDocument)), + currentPosLastRelative(SIZE_MAX), + currentPos(startPos), + currentLine(styler.GetLine(startPos)), + lineEnd(styler.LineEnd(currentLine)), + lineStartNext(styler.LineStart(currentLine + 1)), + atLineStart(static_cast(styler.LineStart(currentLine)) == startPos), + // Mask off all bits which aren't in the chMask. + state(initStyle &chMask) { + + styler.StartAt(startPos /*, chMask*/); + styler.StartSegment(startPos); + + // Variable width is now 0 so GetNextChar gets the char at currentPos into chNext/widthNext + GetNextChar(); + ch = chNext; + width = widthNext; + + GetNextChar(); +} + bool StyleContext::MatchIgnoreCase(const char *s) { if (MakeLowerCase(ch) != static_cast(*s)) return false; diff --git a/scintilla/lexilla/lexlib/StyleContext.h b/scintilla/lexilla/lexlib/StyleContext.h index a6c6f9f4d7..915bbb671f 100644 --- a/scintilla/lexilla/lexlib/StyleContext.h +++ b/scintilla/lexilla/lexlib/StyleContext.h @@ -16,21 +16,21 @@ namespace Lexilla { // syntactically significant. UTF-8 avoids this as all trail bytes are >= 0x80 class StyleContext { LexAccessor &styler; - Scintilla::IDocument *multiByteAccess; - Sci_PositionU endPos; - Sci_PositionU lengthDocument; + Scintilla::IDocument * const multiByteAccess; + const Sci_PositionU lengthDocument; + const Sci_PositionU endPos; + const Sci_Position lineDocEnd; // Used for optimizing GetRelativeCharacter - Sci_PositionU posRelative; + Sci_PositionU posRelative = 0; Sci_PositionU currentPosLastRelative; - Sci_Position offsetRelative; + Sci_Position offsetRelative = 0; void GetNextChar() { if (multiByteAccess) { chNext = multiByteAccess->GetCharacterAndWidth(currentPos+width, &widthNext); } else { chNext = static_cast(styler.SafeGetCharAt(currentPos+width, 0)); - widthNext = 1; } // End of line determined from line end position, allowing CR, LF, // CRLF and Unicode line ends as set by document. @@ -43,59 +43,19 @@ class StyleContext { public: Sci_PositionU currentPos; Sci_Position currentLine; - Sci_Position lineDocEnd; Sci_Position lineEnd; Sci_Position lineStartNext; bool atLineStart; - bool atLineEnd; + bool atLineEnd = false; int state; - int chPrev; - int ch; - Sci_Position width; - int chNext; - Sci_Position widthNext; + int chPrev = 0; + int ch = 0; + Sci_Position width = 0; + int chNext = 0; + Sci_Position widthNext = 1; StyleContext(Sci_PositionU startPos, Sci_PositionU length, - int initStyle, LexAccessor &styler_, char chMask='\377') : - styler(styler_), - multiByteAccess(nullptr), - endPos(startPos + length), - posRelative(0), - currentPosLastRelative(0x7FFFFFFF), - offsetRelative(0), - currentPos(startPos), - currentLine(-1), - lineEnd(-1), - lineStartNext(-1), - atLineEnd(false), - state(initStyle & chMask), // Mask off all bits which aren't in the chMask. - chPrev(0), - ch(0), - width(0), - chNext(0), - widthNext(1) { - if (styler.Encoding() != EncodingType::eightBit) { - multiByteAccess = styler.MultiByteAccess(); - } - styler.StartAt(startPos /*, chMask*/); - styler.StartSegment(startPos); - currentLine = styler.GetLine(startPos); - lineEnd = styler.LineEnd(currentLine); - lineStartNext = styler.LineStart(currentLine+1); - lengthDocument = static_cast(styler.Length()); - if (endPos == lengthDocument) - endPos++; - lineDocEnd = styler.GetLine(lengthDocument); - atLineStart = static_cast(styler.LineStart(currentLine)) == startPos; - - // Variable width is now 0 so GetNextChar gets the char at currentPos into chNext/widthNext - width = 0; - GetNextChar(); - ch = chNext; - width = widthNext; - - GetNextChar(); - } + int initStyle, LexAccessor &styler_, char chMask = '\377'); // Deleted so StyleContext objects can not be copied. StyleContext(const StyleContext &) = delete; StyleContext &operator=(const StyleContext &) = delete; diff --git a/scintilla/lexilla/lexlib/WordList.cxx b/scintilla/lexilla/lexlib/WordList.cxx index f1292317a8..9cddce1e85 100644 --- a/scintilla/lexilla/lexlib/WordList.cxx +++ b/scintilla/lexilla/lexlib/WordList.cxx @@ -24,6 +24,7 @@ namespace { * after each word. */ std::unique_ptr ArrayFromWordList(char *wordlist, size_t slen, size_t *len, bool onlyLineEnds = false) { + assert(wordlist); size_t words = 0; // For rapid determination of whether a character is a separator, build // a look up table. diff --git a/scintilla/lexilla/version.txt b/scintilla/lexilla/version.txt index 54a584dece..82cf079698 100644 --- a/scintilla/lexilla/version.txt +++ b/scintilla/lexilla/version.txt @@ -1 +1 @@ -514 \ No newline at end of file +519 \ No newline at end of file diff --git a/scintilla/src/CallTip.cxx b/scintilla/src/CallTip.cxx index 9fb1b535c4..4742f66e4d 100644 --- a/scintilla/src/CallTip.cxx +++ b/scintilla/src/CallTip.cxx @@ -272,24 +272,14 @@ void CallTip::MouseClick(Point pt) noexcept { } PRectangle CallTip::CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn, - const char *faceName, int size, - int codePage_, CharacterSet characterSet, - Technology technology, - const char *localeName, - const Window &wParent) { + int codePage_, Surface *surfaceMeasure, std::shared_ptr font_) { clickPlace = 0; val = defn; codePage = codePage_; - std::unique_ptr surfaceMeasure = Surface::Allocate(technology); - surfaceMeasure->Init(wParent.GetID()); - surfaceMeasure->SetMode(SurfaceMode(codePage, false)); highlight = Chunk(); inCallTipMode = true; posStartCallTip = pos; - const XYPOSITION deviceHeight = static_cast(surfaceMeasure->DeviceHeightFont(size)); - const FontParameters fp(faceName, deviceHeight / FontSizeMultiplier, FontWeight::Normal, - false, FontQuality::QualityDefault, technology, characterSet, localeName); - font = Font::Allocate(fp); + font = font_; // Look for multiple lines in the text // Only support \n here - simply means container must avoid \r! const int numLines = 1 + static_cast(std::count(val.begin(), val.end(), '\n')); @@ -300,7 +290,7 @@ PRectangle CallTip::CallTipStart(Sci::Position pos, Point pt, int textHeight, co #if !PLAT_CURSES widthArrow = lineHeight * 9 / 10; #endif - const int width = PaintContents(surfaceMeasure.get(), false) + insetX; + const int width = PaintContents(surfaceMeasure, false) + insetX; // The returned // rectangle is aligned to the right edge of the last arrow encountered in @@ -350,7 +340,7 @@ bool CallTip::UseStyleCallTip() const noexcept { // It might be better to have two access functions for this and to use // them for all settings of colours. -void CallTip::SetForeBack(const ColourRGBA &fore, const ColourRGBA &back) noexcept { +void CallTip::SetForeBack(ColourRGBA fore, ColourRGBA back) noexcept { colourBG = back; colourUnSel = fore; } diff --git a/scintilla/src/CallTip.h b/scintilla/src/CallTip.h index dc1071baaa..f8d2946512 100644 --- a/scintilla/src/CallTip.h +++ b/scintilla/src/CallTip.h @@ -71,9 +71,7 @@ class CallTip { /// Setup the calltip and return a rectangle of the area required. PRectangle CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn, - const char *faceName, int size, int codePage_, - Scintilla::CharacterSet characterSet, Scintilla::Technology technology, const char *localeName, - const Window &wParent); + int codePage_, Surface *surfaceMeasure, std::shared_ptr font_); void CallTipCancel() noexcept; @@ -91,7 +89,7 @@ class CallTip { bool UseStyleCallTip() const noexcept; // Modify foreground and background colours - void SetForeBack(const ColourRGBA &fore, const ColourRGBA &back) noexcept; + void SetForeBack(ColourRGBA fore, ColourRGBA back) noexcept; }; } diff --git a/scintilla/src/CaseFolder.cxx b/scintilla/src/CaseFolder.cxx index b5acc5f96f..d66df3f4f1 100644 --- a/scintilla/src/CaseFolder.cxx +++ b/scintilla/src/CaseFolder.cxx @@ -9,53 +9,51 @@ #include #include +#include "CharacterType.h" #include "CaseFolder.h" #include "CaseConvert.h" using namespace Scintilla::Internal; -CaseFolder::~CaseFolder() { +namespace { + +constexpr unsigned char IndexFromChar(char ch) { + return static_cast(ch); +} + } CaseFolderTable::CaseFolderTable() noexcept : mapping{} { - for (size_t iChar=0; iChar(iChar); - } + StandardASCII(); } size_t CaseFolderTable::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { if (lenMixed > sizeFolded) { return 0; - } else { - for (size_t i=0; i(mixed[i])]; - } - return lenMixed; } + for (size_t i=0; i(ch)] = chTranslation; + mapping[IndexFromChar(ch)] = chTranslation; } void CaseFolderTable::StandardASCII() noexcept { - for (size_t iChar=0; iChar= 'A' && iChar <= 'Z') { - mapping[iChar] = static_cast(iChar - 'A' + 'a'); - } else { - mapping[iChar] = static_cast(iChar); - } + for (size_t iChar=0; iChar(MakeLowerCase(iChar)); } } CaseFolderUnicode::CaseFolderUnicode() { - StandardASCII(); converter = ConverterFor(CaseConversion::fold); } size_t CaseFolderUnicode::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) { if ((lenMixed == 1) && (sizeFolded > 0)) { - folded[0] = mapping[static_cast(mixed[0])]; + folded[0] = mapping[IndexFromChar(mixed[0])]; return 1; } else { return converter->CaseConvertString(folded, sizeFolded, mixed, lenMixed); diff --git a/scintilla/src/CaseFolder.h b/scintilla/src/CaseFolder.h index 45abe4d8f5..4f16ed69c4 100644 --- a/scintilla/src/CaseFolder.h +++ b/scintilla/src/CaseFolder.h @@ -12,7 +12,13 @@ namespace Scintilla::Internal { class CaseFolder { public: - virtual ~CaseFolder(); + CaseFolder() = default; + // Deleted so CaseFolder objects can not be copied. + CaseFolder(const CaseFolder &source) = delete; + CaseFolder(CaseFolder &&) = delete; + CaseFolder &operator=(const CaseFolder &) = delete; + CaseFolder &operator=(CaseFolder &&) = delete; + virtual ~CaseFolder() = default; virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) = 0; }; diff --git a/scintilla/src/CellBuffer.cxx b/scintilla/src/CellBuffer.cxx index b84372cf2b..be018bbd6b 100644 --- a/scintilla/src/CellBuffer.cxx +++ b/scintilla/src/CellBuffer.cxx @@ -27,6 +27,9 @@ #include "Position.h" #include "SplitVector.h" #include "Partitioning.h" +#include "RunStyles.h" +#include "SparseVector.h" +#include "ChangeHistory.h" #include "CellBuffer.h" #include "UniConversion.h" @@ -91,6 +94,11 @@ using namespace Scintilla::Internal; template class LineStartIndex { + // line_cast(): cast Sci::Line to either 32-bit or 64-bit value + // This avoids warnings from Visual C++ Code Analysis and shortens code + static constexpr POS line_cast(Sci::Line pos) noexcept { + return static_cast(pos); + } public: int refCount; Partitioning starts; @@ -98,20 +106,13 @@ class LineStartIndex { LineStartIndex() : refCount(0), starts(4) { // Minimal initial allocation } - // Deleted so LineStartIndex objects can not be copied. - LineStartIndex(const LineStartIndex &) = delete; - LineStartIndex(LineStartIndex &&) = delete; - void operator=(const LineStartIndex &) = delete; - void operator=(LineStartIndex &&) = delete; - virtual ~LineStartIndex() { - } bool Allocate(Sci::Line lines) { refCount++; Sci::Position length = starts.PositionFromPartition(starts.Partitions()); for (Sci::Line line = starts.Partitions(); line < lines; line++) { // Produce an ascending sequence that will be filled in with correct widths later length++; - starts.InsertPartition(static_cast(line), static_cast(length)); + starts.InsertPartition(line_cast(line), line_cast(length)); } return refCount == 1; } @@ -126,12 +127,12 @@ class LineStartIndex { return refCount > 0; } Sci::Position LineWidth(Sci::Line line) const noexcept { - return starts.PositionFromPartition(static_cast(line) + 1) - - starts.PositionFromPartition(static_cast(line)); + return starts.PositionFromPartition(line_cast(line) + 1) - + starts.PositionFromPartition(line_cast(line)); } void SetLineWidth(Sci::Line line, Sci::Position width) noexcept { const Sci::Position widthCurrent = LineWidth(line); - starts.InsertText(static_cast(line), static_cast(width - widthCurrent)); + starts.InsertText(line_cast(line), line_cast(width - widthCurrent)); } void AllocateLines(Sci::Line lines) { if (lines > starts.Partitions()) { @@ -141,9 +142,9 @@ class LineStartIndex { void InsertLines(Sci::Line line, Sci::Line lines) { // Insert multiple lines with each temporarily 1 character wide. // The line widths will be fixed up by later measuring code. - const POS lineAsPos = static_cast(line); + const POS lineAsPos = line_cast(line); const POS lineStart = starts.PositionFromPartition(lineAsPos - 1) + 1; - for (POS l = 0; l < static_cast(lines); l++) { + for (POS l = 0; l < line_cast(lines); l++) { starts.InsertPartition(lineAsPos + l, lineStart + l); } } @@ -163,16 +164,21 @@ class LineVector : public ILineVector { | (startsUTF16.Active() ? LineCharacterIndexType::Utf16 : LineCharacterIndexType::None); } + // pos_cast(): cast Sci::Line and Sci::Position to either 32-bit or 64-bit value + // This avoids warnings from Visual C++ Code Analysis and shortens code + static constexpr POS pos_cast(Sci::Position pos) noexcept { + return static_cast(pos); + } + + // line_from_pos_cast(): return 32-bit or 64-bit value as Sci::Line + // This avoids warnings from Visual C++ Code Analysis and shortens code + static constexpr Sci::Line line_from_pos_cast(POS line) noexcept { + return static_cast(line); + } + public: LineVector() : starts(256), perLine(nullptr), activeIndices(LineCharacterIndexType::None) { } - // Deleted so LineVector objects can not be copied. - LineVector(const LineVector &) = delete; - LineVector(LineVector &&) = delete; - LineVector &operator=(const LineVector &) = delete; - LineVector &operator=(LineVector &&) = delete; - ~LineVector() override { - } void Init() override { starts.DeleteAll(); if (perLine) { @@ -185,11 +191,11 @@ class LineVector : public ILineVector { perLine = pl; } void InsertText(Sci::Line line, Sci::Position delta) noexcept override { - starts.InsertText(static_cast(line), static_cast(delta)); + starts.InsertText(pos_cast(line), pos_cast(delta)); } void InsertLine(Sci::Line line, Sci::Position position, bool lineStart) override { - const POS lineAsPos = static_cast(line); - starts.InsertPartition(lineAsPos, static_cast(position)); + const POS lineAsPos = pos_cast(line); + starts.InsertPartition(lineAsPos, pos_cast(position)); if (activeIndices != LineCharacterIndexType::None) { if (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) { startsUTF32.InsertLines(line, 1); @@ -205,7 +211,7 @@ class LineVector : public ILineVector { } } void InsertLines(Sci::Line line, const Sci::Position *positions, size_t lines, bool lineStart) override { - const POS lineAsPos = static_cast(line); + const POS lineAsPos = pos_cast(line); if constexpr (sizeof(Sci::Position) == sizeof(POS)) { starts.InsertPartitions(lineAsPos, positions, lines); } else { @@ -226,22 +232,22 @@ class LineVector : public ILineVector { } } void SetLineStart(Sci::Line line, Sci::Position position) noexcept override { - starts.SetPartitionStartPosition(static_cast(line), static_cast(position)); + starts.SetPartitionStartPosition(pos_cast(line), pos_cast(position)); } void RemoveLine(Sci::Line line) override { - starts.RemovePartition(static_cast(line)); + starts.RemovePartition(pos_cast(line)); if (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) { - startsUTF32.starts.RemovePartition(static_cast(line)); + startsUTF32.starts.RemovePartition(pos_cast(line)); } if (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) { - startsUTF16.starts.RemovePartition(static_cast(line)); + startsUTF16.starts.RemovePartition(pos_cast(line)); } if (perLine) { perLine->RemoveLine(line); } } Sci::Line Lines() const noexcept override { - return static_cast(starts.Partitions()); + return line_from_pos_cast(starts.Partitions()); } void AllocateLines(Sci::Line lines) override { if (lines > Lines()) { @@ -255,17 +261,17 @@ class LineVector : public ILineVector { } } Sci::Line LineFromPosition(Sci::Position pos) const noexcept override { - return static_cast(starts.PartitionFromPosition(static_cast(pos))); + return line_from_pos_cast(starts.PartitionFromPosition(pos_cast(pos))); } Sci::Position LineStart(Sci::Line line) const noexcept override { - return starts.PositionFromPartition(static_cast(line)); + return starts.PositionFromPartition(pos_cast(line)); } void InsertCharacters(Sci::Line line, CountWidths delta) noexcept override { if (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) { - startsUTF32.starts.InsertText(static_cast(line), static_cast(delta.WidthUTF32())); + startsUTF32.starts.InsertText(pos_cast(line), pos_cast(delta.WidthUTF32())); } if (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) { - startsUTF16.starts.InsertText(static_cast(line), static_cast(delta.WidthUTF16())); + startsUTF16.starts.InsertText(pos_cast(line), pos_cast(delta.WidthUTF16())); } } void SetLineCharactersWidth(Sci::Line line, CountWidths width) noexcept override { @@ -308,16 +314,16 @@ class LineVector : public ILineVector { } Sci::Position IndexLineStart(Sci::Line line, LineCharacterIndexType lineCharacterIndex) const noexcept override { if (lineCharacterIndex == LineCharacterIndexType::Utf32) { - return startsUTF32.starts.PositionFromPartition(static_cast(line)); + return startsUTF32.starts.PositionFromPartition(pos_cast(line)); } else { - return startsUTF16.starts.PositionFromPartition(static_cast(line)); + return startsUTF16.starts.PositionFromPartition(pos_cast(line)); } } Sci::Line LineFromPositionIndex(Sci::Position pos, LineCharacterIndexType lineCharacterIndex) const noexcept override { if (lineCharacterIndex == LineCharacterIndexType::Utf32) { - return static_cast(startsUTF32.starts.PartitionFromPosition(static_cast(pos))); + return line_from_pos_cast(startsUTF32.starts.PartitionFromPosition(pos_cast(pos))); } else { - return static_cast(startsUTF16.starts.PartitionFromPosition(static_cast(pos))); + return line_from_pos_cast(startsUTF16.starts.PartitionFromPosition(pos_cast(pos))); } } }; @@ -329,9 +335,6 @@ Action::Action() noexcept { mayCoalesce = false; } -Action::~Action() { -} - void Action::Create(ActionType at_, Sci::Position position_, const char *data_, Sci::Position lenData_, bool mayCoalesce_) { data = nullptr; position = position_; @@ -379,9 +382,6 @@ UndoHistory::UndoHistory() { actions[currentAction].Create(ActionType::start); } -UndoHistory::~UndoHistory() { -} - void UndoHistory::EnsureUndoRoom() { // Have to test that there is room for 2 more actions in the array // as two actions may be created by the calling function @@ -399,12 +399,17 @@ const char *UndoHistory::AppendAction(ActionType at, Sci::Position position, con // actions[currentAction - 1].position, actions[currentAction - 1].lenData); if (currentAction < savePoint) { savePoint = -1; + if (!detach) { + detach = currentAction; + } + } else if (detach && (*detach > currentAction)) { + detach = currentAction; } int oldCurrentAction = currentAction; if (currentAction >= 1) { if (0 == undoSequenceDepth) { // Top level actions may not always be coalesced - int targetAct = -1; + ptrdiff_t targetAct = -1; const Action *actPrevious = &(actions[currentAction + targetAct]); // Container actions may forward the coalesce state of Scintilla Actions. while ((actPrevious->at == ActionType::container) && actPrevious->mayCoalesce) { @@ -506,12 +511,29 @@ void UndoHistory::DeleteUndoHistory() { void UndoHistory::SetSavePoint() noexcept { savePoint = currentAction; + detach.reset(); } bool UndoHistory::IsSavePoint() const noexcept { return savePoint == currentAction; } +bool UndoHistory::BeforeSavePoint() const noexcept { + return (savePoint < 0) || (savePoint > currentAction); +} + +bool UndoHistory::BeforeReachableSavePoint() const noexcept { + return (savePoint >= 0) && !detach && (savePoint > currentAction); +} + +bool UndoHistory::AfterSavePoint() const noexcept { + return (savePoint >= 0) && (savePoint <= currentAction); +} + +bool UndoHistory::AfterDetachPoint() const noexcept { + return detach && (*detach < currentAction); +} + void UndoHistory::TentativeStart() { tentativePoint = currentAction; } @@ -598,8 +620,7 @@ CellBuffer::CellBuffer(bool hasStyles_, bool largeDocument_) : plv = std::make_unique>(); } -CellBuffer::~CellBuffer() { -} +CellBuffer::~CellBuffer() noexcept = default; char CellBuffer::CharAt(Sci::Position position) const noexcept { return substance.ValueAt(position); @@ -686,6 +707,9 @@ const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci: } BasicInsertString(position, s, insertLength); + if (changeHistory) { + changeHistory->Insert(position, insertLength, collectingUndo, uh.BeforeReachableSavePoint()); + } } return data; } @@ -734,6 +758,11 @@ const char *CellBuffer::DeleteChars(Sci::Position position, Sci::Position delete data = uh.AppendAction(ActionType::remove, position, data, deleteLength, startSequence); } + if (changeHistory) { + changeHistory->DeleteRangeSavingHistory(position, deleteLength, + uh.BeforeReachableSavePoint(), uh.AfterDetachPoint()); + } + BasicDeleteChars(position, deleteLength); } return data; @@ -849,6 +878,9 @@ bool CellBuffer::HasStyles() const noexcept { void CellBuffer::SetSavePoint() { uh.SetSavePoint(); + if (changeHistory) { + changeHistory->SetSavePoint(); + } } bool CellBuffer::IsSavePoint() const noexcept { @@ -1308,14 +1340,24 @@ const Action &CellBuffer::GetUndoStep() const { void CellBuffer::PerformUndoStep() { const Action &actionStep = uh.GetUndoStep(); + if (changeHistory && uh.BeforeSavePoint()) { + changeHistory->StartReversion(); + } if (actionStep.at == ActionType::insert) { if (substance.Length() < actionStep.lenData) { throw std::runtime_error( "CellBuffer::PerformUndoStep: deletion must be less than document length."); } + if (changeHistory) { + changeHistory->DeleteRange(actionStep.position, actionStep.lenData, + uh.BeforeSavePoint() && !uh.AfterDetachPoint()); + } BasicDeleteChars(actionStep.position, actionStep.lenData); } else if (actionStep.at == ActionType::remove) { BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData); + if (changeHistory) { + changeHistory->UndoDeleteStep(actionStep.position, actionStep.lenData, uh.AfterDetachPoint()); + } } uh.CompletedUndoStep(); } @@ -1336,9 +1378,57 @@ void CellBuffer::PerformRedoStep() { const Action &actionStep = uh.GetRedoStep(); if (actionStep.at == ActionType::insert) { BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData); + if (changeHistory) { + changeHistory->Insert(actionStep.position, actionStep.lenData, collectingUndo, + uh.BeforeSavePoint() && !uh.AfterDetachPoint()); + } } else if (actionStep.at == ActionType::remove) { + if (changeHistory) { + changeHistory->DeleteRangeSavingHistory(actionStep.position, actionStep.lenData, + uh.BeforeReachableSavePoint(), uh.AfterDetachPoint()); + } BasicDeleteChars(actionStep.position, actionStep.lenData); } + if (changeHistory && uh.AfterSavePoint()) { + changeHistory->EndReversion(); + } uh.CompletedRedoStep(); } +void CellBuffer::ChangeHistorySet(bool set) { + if (set) { + if (!changeHistory) { + changeHistory = std::make_unique(Length()); + } + } else { + changeHistory.reset(); + } +} + +int CellBuffer::EditionAt(Sci::Position pos) const noexcept { + if (changeHistory) { + return changeHistory->EditionAt(pos); + } + return 0; +} + +Sci::Position CellBuffer::EditionEndRun(Sci::Position pos) const noexcept { + if (changeHistory) { + return changeHistory->EditionEndRun(pos); + } + return Length(); +} + +unsigned int CellBuffer::EditionDeletesAt(Sci::Position pos) const noexcept { + if (changeHistory) { + return changeHistory->EditionDeletesAt(pos); + } + return 0; +} + +Sci::Position CellBuffer::EditionNextDelete(Sci::Position pos) const noexcept { + if (changeHistory) { + return changeHistory->EditionNextDelete(pos); + } + return Length() + 1; +} diff --git a/scintilla/src/CellBuffer.h b/scintilla/src/CellBuffer.h index d8914d7baf..7f0b87c4d1 100644 --- a/scintilla/src/CellBuffer.h +++ b/scintilla/src/CellBuffer.h @@ -20,6 +20,7 @@ class PerLine { virtual void RemoveLine(Sci::Line line)=0; }; +class ChangeHistory; /** * The line vector contains information about each of the lines in a cell buffer. */ @@ -39,13 +40,6 @@ class Action { bool mayCoalesce; Action() noexcept; - // Deleted so Action objects can not be copied. - Action(const Action &other) = delete; - Action &operator=(const Action &other) = delete; - Action &operator=(const Action &&other) = delete; - // Move constructor allows vector to be resized without reallocating. - Action(Action &&other) noexcept = default; - ~Action(); void Create(ActionType at_, Sci::Position position_=0, const char *data_=nullptr, Sci::Position lenData_=0, bool mayCoalesce_=true); void Clear() noexcept; }; @@ -60,17 +54,12 @@ class UndoHistory { int undoSequenceDepth; int savePoint; int tentativePoint; + std::optional detach; void EnsureUndoRoom(); public: UndoHistory(); - // Deleted so UndoHistory objects can not be copied. - UndoHistory(const UndoHistory &) = delete; - UndoHistory(UndoHistory &&) = delete; - void operator=(const UndoHistory &) = delete; - void operator=(UndoHistory &&) = delete; - ~UndoHistory(); const char *AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true); @@ -83,6 +72,10 @@ class UndoHistory { /// the buffer was saved. Undo and redo can move over the save point. void SetSavePoint() noexcept; bool IsSavePoint() const noexcept; + bool BeforeSavePoint() const noexcept; + bool BeforeReachableSavePoint() const noexcept; + bool AfterSavePoint() const noexcept; + bool AfterDetachPoint() const noexcept; // Tentative actions are used for input composition so that it can be undone cleanly void TentativeStart(); @@ -146,6 +139,8 @@ class CellBuffer { bool collectingUndo; UndoHistory uh; + std::unique_ptr changeHistory; + std::unique_ptr plv; bool UTF8LineEndOverlaps(Sci::Position position) const noexcept; @@ -163,9 +158,9 @@ class CellBuffer { // Deleted so CellBuffer objects can not be copied. CellBuffer(const CellBuffer &) = delete; CellBuffer(CellBuffer &&) = delete; - void operator=(const CellBuffer &) = delete; - void operator=(CellBuffer &&) = delete; - ~CellBuffer(); + CellBuffer &operator=(const CellBuffer &) = delete; + CellBuffer &operator=(CellBuffer &&) = delete; + ~CellBuffer() noexcept; /// Retrieving positions outside the range of the buffer works and returns 0 char CharAt(Sci::Position position) const noexcept; @@ -237,6 +232,12 @@ class CellBuffer { int StartRedo(); const Action &GetRedoStep() const; void PerformRedoStep(); + + void ChangeHistorySet(bool set); + [[nodiscard]] int EditionAt(Sci::Position pos) const noexcept; + [[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept; + [[nodiscard]] unsigned int EditionDeletesAt(Sci::Position pos) const noexcept; + [[nodiscard]] Sci::Position EditionNextDelete(Sci::Position pos) const noexcept; }; } diff --git a/scintilla/src/ChangeHistory.cxx b/scintilla/src/ChangeHistory.cxx new file mode 100644 index 0000000000..4c771a93cb --- /dev/null +++ b/scintilla/src/ChangeHistory.cxx @@ -0,0 +1,422 @@ +// Scintilla source code edit control +/** @file ChangeHistory.cxx + ** Manages a history of changes in a document. + **/ +// Copyright 2022 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ScintillaTypes.h" + +#include "Debugging.h" + +#include "Position.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "SparseVector.h" +#include "ChangeHistory.h" + +namespace Scintilla::Internal { + +void ChangeStack::Clear() noexcept { + steps.clear(); + insertions.clear(); +} + +void ChangeStack::AddStep() { + steps.push_back(0); +} + +void ChangeStack::PushDeletion(Sci::Position positionDeletion, int edition) { + steps.back()++; + insertions.push_back({ positionDeletion, 0, edition, InsertionSpan::Direction::deletion }); +} + +void ChangeStack::PushInsertion(Sci::Position positionInsertion, Sci::Position length, int edition) { + steps.back()++; + insertions.push_back({ positionInsertion, length, edition, InsertionSpan::Direction::insertion }); +}; + +size_t ChangeStack::PopStep() noexcept { + const size_t spans = steps.back(); + steps.pop_back(); + return spans; +} + +InsertionSpan ChangeStack::PopSpan() noexcept { + const InsertionSpan span = insertions.back(); + insertions.pop_back(); + return span; +} + +void ChangeStack::SetSavePoint() noexcept { + // Switch changeUnsaved to changeSaved + for (InsertionSpan &x : insertions) { + if (x.edition == changeModified) { + x.edition = changeSaved; + } + } +} + +void ChangeLog::Clear(Sci::Position length) { + changeStack.Clear(); + insertEdition.DeleteAll(); + deleteEdition.DeleteAll(); + InsertSpace(0, length); +} + +void ChangeLog::InsertSpace(Sci::Position position, Sci::Position insertLength) { + assert(insertEdition.Length() == deleteEdition.Length()); + insertEdition.InsertSpace(position, insertLength); + deleteEdition.InsertSpace(position, insertLength); +} + +void ChangeLog::DeleteRange(Sci::Position position, Sci::Position deleteLength) { + insertEdition.DeleteRange(position, deleteLength); + const EditionSetOwned &editions = deleteEdition.ValueAt(position); + if (editions) { + const EditionSet savedEditions = *editions; + deleteEdition.DeleteRange(position, deleteLength); + EditionSetOwned reset = std::make_unique(savedEditions); + deleteEdition.SetValueAt(position, std::move(reset)); + } else { + deleteEdition.DeleteRange(position, deleteLength); + } + assert(insertEdition.Length() == deleteEdition.Length()); +} + +void ChangeLog::Insert(Sci::Position start, Sci::Position length, int edition) { + insertEdition.FillRange(start, edition, length); +} + +void ChangeLog::CollapseRange(Sci::Position position, Sci::Position deleteLength) { + const Sci::Position positionMax = position + deleteLength; + Sci::Position positionDeletion = position + 1; + while (positionDeletion <= positionMax) { + const EditionSetOwned &editions = deleteEdition.ValueAt(positionDeletion); + if (editions) { + for (const int ed : *editions) { + PushDeletionAt(position, ed); + } + EditionSetOwned empty; + deleteEdition.SetValueAt(positionDeletion, std::move(empty)); + } + positionDeletion = deleteEdition.PositionNext(positionDeletion); + } +} + +void ChangeLog::PushDeletionAt(Sci::Position position, int edition) { + if (!deleteEdition.ValueAt(position)) { + deleteEdition.SetValueAt(position, std::make_unique()); + } + deleteEdition.ValueAt(position)->push_back(edition); +} + +void ChangeLog::InsertFrontDeletionAt(Sci::Position position, int edition) { + if (!deleteEdition.ValueAt(position)) { + deleteEdition.SetValueAt(position, std::make_unique()); + } + const EditionSetOwned &editions = deleteEdition.ValueAt(position); + editions->insert(editions->begin(), edition); +} + +void ChangeLog::SaveRange(Sci::Position position, Sci::Position length) { + // Save insertEdition range into undo stack + changeStack.AddStep(); + Sci::Position positionInsertion = position; + const ptrdiff_t editionStart = insertEdition.ValueAt(positionInsertion); + if (editionStart == 0) { + positionInsertion = insertEdition.EndRun(positionInsertion); + } + const Sci::Position positionMax = position + length; + while (positionInsertion < positionMax) { + const Sci::Position positionEndInsertion = insertEdition.EndRun(positionInsertion); + changeStack.PushInsertion(positionInsertion, std::min(positionEndInsertion, positionMax) - positionInsertion, + insertEdition.ValueAt(positionInsertion)); + positionInsertion = insertEdition.EndRun(positionEndInsertion); + } + Sci::Position positionDeletion = position + 1; + while (positionDeletion <= positionMax) { + const EditionSetOwned &editions = deleteEdition.ValueAt(positionDeletion); + if (editions) { + for (const int ed : *editions) { + changeStack.PushDeletion(positionDeletion, ed); + } + } + positionDeletion = deleteEdition.PositionNext(positionDeletion); + } +} + +void ChangeLog::PopDeletion(Sci::Position position, Sci::Position deleteLength) { + // Just performed InsertSpace(position, deleteLength) so *this* element in + // deleteEdition moved forward by deleteLength + EditionSetOwned eso = deleteEdition.Extract(position + deleteLength); + deleteEdition.SetValueAt(position, std::move(eso)); + const EditionSetOwned &editions = deleteEdition.ValueAt(position); + assert(editions); + editions->pop_back(); + const size_t inserts = changeStack.PopStep(); + for (size_t i = 0; i < inserts; i++) { + const InsertionSpan span = changeStack.PopSpan(); + if (span.direction == InsertionSpan::Direction::insertion) { + insertEdition.FillRange(span.start, span.edition, span.length); + } else { + assert(editions); + assert(editions->back() == span.edition); + editions->pop_back(); + InsertFrontDeletionAt(span.start, span.edition); + } + } + + if (editions->empty()) { + deleteEdition.SetValueAt(position, EditionSetOwned{}); + } +} + +void ChangeLog::SaveHistoryForDelete(Sci::Position position, Sci::Position deleteLength) { + assert(position >= 0); + assert(deleteLength >= 0); + assert(position + deleteLength <= Length()); + SaveRange(position, deleteLength); + CollapseRange(position, deleteLength); +} + +void ChangeLog::DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength) { + SaveHistoryForDelete(position, deleteLength); + DeleteRange(position, deleteLength); +} + +void ChangeLog::SetSavePoint() { + // Switch changeUnsaved to changeSaved + changeStack.SetSavePoint(); + + const Sci::Position length = insertEdition.Length(); + + for (Sci::Position startRun = 0; startRun < length;) { + const Sci::Position endRun = insertEdition.EndRun(startRun); + if (insertEdition.ValueAt(startRun) == changeModified) { + insertEdition.FillRange(startRun, changeSaved, endRun - startRun); + } + startRun = endRun; + } + + for (Sci::Position positionDeletion = 0; positionDeletion <= length;) { + const EditionSetOwned &editions = deleteEdition.ValueAt(positionDeletion); + if (editions) { + for (int &ed : *editions) { + if (ed == changeModified) { + ed = changeSaved; + } + } + } + positionDeletion = deleteEdition.PositionNext(positionDeletion); + } +} + +Sci::Position ChangeLog::Length() const noexcept { + return insertEdition.Length(); +} + +size_t ChangeLog::DeletionCount(Sci::Position start, Sci::Position length) const noexcept { + const Sci::Position end = start + length; + size_t count = 0; + while (start <= end) { + const EditionSetOwned &editions = deleteEdition.ValueAt(start); + if (editions) { + count += editions->size(); + } + start = deleteEdition.PositionNext(start); + } + return count; +} + +void ChangeLog::Check() const noexcept { + assert(insertEdition.Length() == deleteEdition.Length()); +} + +ChangeHistory::ChangeHistory(Sci::Position length) { + changeLog.Clear(length); +} + +void ChangeHistory::Insert(Sci::Position position, Sci::Position insertLength, bool collectingUndo, bool beforeSave) { + Check(); + changeLog.InsertSpace(position, insertLength); + const int edition = collectingUndo ? (beforeSave ? changeSaved : changeModified) : + changeOriginal; + changeLog.Insert(position, insertLength, edition); + if (changeLogReversions) { + changeLogReversions->InsertSpace(position, insertLength); + if (beforeSave) { + changeLogReversions->PopDeletion(position, insertLength); + } + } + Check(); +} + +void ChangeHistory::DeleteRange(Sci::Position position, Sci::Position deleteLength, bool reverting) { + Check(); + assert(DeletionCount(position, deleteLength-1) == 0); + changeLog.DeleteRange(position, deleteLength); + if (changeLogReversions) { + changeLogReversions->DeleteRangeSavingHistory(position, deleteLength); + if (reverting) { + changeLogReversions->PushDeletionAt(position, 1); + } + } + Check(); +} + +void ChangeHistory::DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength, bool beforeSave, bool isDetached) { + changeLog.DeleteRangeSavingHistory(position, deleteLength); + changeLog.PushDeletionAt(position, beforeSave ? changeSaved : changeModified); + if (changeLogReversions) { + if (isDetached) { + changeLogReversions->SaveHistoryForDelete(position, deleteLength); + } + changeLogReversions->DeleteRange(position, deleteLength); + } + Check(); +} + +void ChangeHistory::StartReversion() { + if (!changeLogReversions) { + changeLogReversions = std::make_unique(); + changeLogReversions->Clear(changeLog.Length()); + } + Check(); +} + +void ChangeHistory::EndReversion() noexcept { + changeLogReversions.reset(); + Check(); +} + +void ChangeHistory::SetSavePoint() { + changeLog.SetSavePoint(); + EndReversion(); +} + +void ChangeHistory::UndoDeleteStep(Sci::Position position, Sci::Position deleteLength, bool isDetached) { + Check(); + changeLog.InsertSpace(position, deleteLength); + changeLog.PopDeletion(position, deleteLength); + if (changeLogReversions) { + changeLogReversions->InsertSpace(position, deleteLength); + if (!isDetached) { + changeLogReversions->Insert(position, deleteLength, 1); + } + } + Check(); +} + +Sci::Position ChangeHistory::Length() const noexcept { + return changeLog.Length(); +} + +void ChangeHistory::SetEpoch(int epoch) noexcept { + historicEpoch = epoch; +} + +void ChangeHistory::EditionCreateHistory(Sci::Position start, Sci::Position length) { + if (start <= changeLog.Length()) { + if (length) { + changeLog.insertEdition.FillRange(start, historicEpoch, length); + } else { + changeLog.PushDeletionAt(start, historicEpoch); + } + } +} + +// Editions: +// <0 History +// 0 Original unchanged +// 1 Reverted to origin +// 2 Saved +// 3 Unsaved +// 4 Reverted to change +int ChangeHistory::EditionAt(Sci::Position pos) const noexcept { + const int edition = changeLog.insertEdition.ValueAt(pos); + if (changeLogReversions) { + const int editionReversion = changeLogReversions->insertEdition.ValueAt(pos); + if (editionReversion) { + if (edition < 0) + return 1; + return edition ? 4 : 1; + } + } + return edition; +} + +Sci::Position ChangeHistory::EditionEndRun(Sci::Position pos) const noexcept { + if (changeLogReversions) { + assert(changeLogReversions->Length() == changeLog.Length()); + const Sci::Position nextReversion = changeLogReversions->insertEdition.EndRun(pos); + const Sci::Position next = changeLog.insertEdition.EndRun(pos); + return std::min(next, nextReversion); + } + return changeLog.insertEdition.EndRun(pos); +} + +// Produce a 4-bit value from the deletions at a position +unsigned int ChangeHistory::EditionDeletesAt(Sci::Position pos) const noexcept { + unsigned int editionSet = 0; + const EditionSetOwned &editionSetDeletions = changeLog.deleteEdition.ValueAt(pos); + if (editionSetDeletions) { + for (const unsigned int ed : *editionSetDeletions) { + editionSet = editionSet | (1u << (ed-1)); + } + } + if (changeLogReversions) { + const EditionSetOwned &editionSetReversions = changeLogReversions->deleteEdition.ValueAt(pos); + if (editionSetReversions) { + // If there is no saved or modified -> revertedToOrigin + if (!(editionSet & (bitSaved | bitModified))) { + editionSet = editionSet | bitRevertedToOriginal; + } else { + editionSet = editionSet | bitRevertedToModified; + } + } + } + return editionSet; +} + +Sci::Position ChangeHistory::EditionNextDelete(Sci::Position pos) const noexcept { + const Sci::Position next = changeLog.deleteEdition.PositionNext(pos); + if (changeLogReversions) { + const Sci::Position nextReversion = changeLogReversions->deleteEdition.PositionNext(pos); + return std::min(next, nextReversion); + } + return next; +} + +size_t ChangeHistory::DeletionCount(Sci::Position start, Sci::Position length) const noexcept { + return changeLog.DeletionCount(start, length); +} + +EditionSet ChangeHistory::DeletionsAt(Sci::Position pos) const { + const EditionSetOwned &editions = changeLog.deleteEdition.ValueAt(pos); + if (editions) { + return *editions; + } + return {}; +} + +void ChangeHistory::Check() noexcept { + changeLog.Check(); + if (changeLogReversions) { + changeLogReversions->Check(); + assert(changeLogReversions->Length() == changeLog.Length()); + } +} + +} diff --git a/scintilla/src/ChangeHistory.h b/scintilla/src/ChangeHistory.h new file mode 100644 index 0000000000..8a6e745dca --- /dev/null +++ b/scintilla/src/ChangeHistory.h @@ -0,0 +1,112 @@ +// Scintilla source code edit control +/** @file ChangeHistory.h + ** Manages a history of changes in a document. + **/ +// Copyright 2022 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CHANGEHISTORY_H +#define CHANGEHISTORY_H + +namespace Scintilla::Internal { + +constexpr int changeOriginal = 0; +constexpr int changeRevertedOriginal = 1; +constexpr int changeSaved = 2; +constexpr int changeModified = 3; +constexpr int changeRevertedToChange = 4; + +// As bit flags +constexpr unsigned int bitRevertedToOriginal = 1; +constexpr unsigned int bitSaved = 2; +constexpr unsigned int bitModified = 4; +constexpr unsigned int bitRevertedToModified = 8; + +struct InsertionSpan { + Sci::Position start; + Sci::Position length; + int edition; + enum class Direction { insertion, deletion } direction; +}; + +using EditionSet = std::vector; +using EditionSetOwned = std::unique_ptr; + +class ChangeStack { + std::vector steps; + std::vector insertions; +public: + void Clear() noexcept; + void AddStep(); + void PushDeletion(Sci::Position positionDeletion, int edition); + void PushInsertion(Sci::Position positionInsertion, Sci::Position length, int edition); + [[nodiscard]] size_t PopStep() noexcept; + [[nodiscard]] InsertionSpan PopSpan() noexcept; + void SetSavePoint() noexcept; +}; + +struct ChangeLog { + ChangeStack changeStack; + RunStyles insertEdition; + SparseVector deleteEdition; + + void Clear(Sci::Position length); + void InsertSpace(Sci::Position position, Sci::Position insertLength); + void DeleteRange(Sci::Position position, Sci::Position deleteLength); + void Insert(Sci::Position start, Sci::Position length, int edition); + void CollapseRange(Sci::Position position, Sci::Position deleteLength); + void PushDeletionAt(Sci::Position position, int edition); + void InsertFrontDeletionAt(Sci::Position position, int edition); + void SaveRange(Sci::Position position, Sci::Position length); + void PopDeletion(Sci::Position position, Sci::Position deleteLength); + void SaveHistoryForDelete(Sci::Position position, Sci::Position deleteLength); + void DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength); + void SetSavePoint(); + + Sci::Position Length() const noexcept; + [[nodiscard]] size_t DeletionCount(Sci::Position start, Sci::Position length) const noexcept; + void Check() const noexcept; +}; + +enum class ReversionState { clear, reverting, detached }; + +class ChangeHistory { + ChangeLog changeLog; + std::unique_ptr changeLogReversions; + int historicEpoch = -1; + +public: + ChangeHistory(Sci::Position length=0); + + void Insert(Sci::Position position, Sci::Position insertLength, bool collectingUndo, bool beforeSave); + void DeleteRange(Sci::Position position, Sci::Position deleteLength, bool reverting); + void DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength, bool beforeSave, bool isDetached); + + void StartReversion(); + void EndReversion() noexcept; + + void SetSavePoint(); + + void UndoDeleteStep(Sci::Position position, Sci::Position deleteLength, bool isDetached); + + [[nodiscard]] Sci::Position Length() const noexcept; + + // Setting up history before this session + void SetEpoch(int epoch) noexcept; + void EditionCreateHistory(Sci::Position start, Sci::Position length); + + // Queries for drawing + [[nodiscard]] int EditionAt(Sci::Position pos) const noexcept; + [[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept; + [[nodiscard]] unsigned int EditionDeletesAt(Sci::Position pos) const noexcept; + [[nodiscard]] Sci::Position EditionNextDelete(Sci::Position pos) const noexcept; + + // Testing - not used by Scintilla + [[nodiscard]] size_t DeletionCount(Sci::Position start, Sci::Position length) const noexcept; + EditionSet DeletionsAt(Sci::Position pos) const; + void Check() noexcept; +}; + +} + +#endif diff --git a/scintilla/src/ContractionState.cxx b/scintilla/src/ContractionState.cxx index 8b4babb2ee..b36e1ebd84 100644 --- a/scintilla/src/ContractionState.cxx +++ b/scintilla/src/ContractionState.cxx @@ -51,14 +51,14 @@ class ContractionState final : public IContractionState { void InsertLine(Sci::Line lineDoc); void DeleteLine(Sci::Line lineDoc); + // line_cast(): cast Sci::Line to either 32-bit or 64-bit value + // This avoids warnings from Visual C++ Code Analysis and shortens code + static constexpr LINE line_cast(Sci::Line line) noexcept { + return static_cast(line); + } + public: ContractionState() noexcept; - // Deleted so ContractionState objects can not be copied. - ContractionState(const ContractionState &) = delete; - void operator=(const ContractionState &) = delete; - ContractionState(ContractionState &&) = delete; - void operator=(ContractionState &&) = delete; - ~ContractionState() override; void Clear() noexcept override; @@ -80,6 +80,7 @@ class ContractionState final : public IContractionState { bool GetExpanded(Sci::Line lineDoc) const noexcept override; bool SetExpanded(Sci::Line lineDoc, bool isExpanded) override; + bool ExpandAll() override; Sci::Line ContractedNext(Sci::Line lineDocStart) const noexcept override; int GetHeight(Sci::Line lineDoc) const noexcept override; @@ -94,9 +95,6 @@ template ContractionState::ContractionState() noexcept : linesInDocument(1) { } -template -ContractionState::~ContractionState() = default; - template void ContractionState::EnsureData() { if (OneToOne()) { @@ -114,7 +112,7 @@ void ContractionState::InsertLine(Sci::Line lineDoc) { if (OneToOne()) { linesInDocument++; } else { - const LINE lineDocCast = static_cast(lineDoc); + const LINE lineDocCast = line_cast(lineDoc); visible->InsertSpace(lineDocCast, 1); visible->SetValueAt(lineDocCast, 1); expanded->InsertSpace(lineDocCast, 1); @@ -124,7 +122,7 @@ void ContractionState::InsertLine(Sci::Line lineDoc) { foldDisplayTexts->InsertSpace(lineDocCast, 1); foldDisplayTexts->SetValueAt(lineDocCast, nullptr); const Sci::Line lineDisplay = DisplayFromDoc(lineDoc); - displayLines->InsertPartition(lineDocCast, static_cast(lineDisplay)); + displayLines->InsertPartition(lineDocCast, line_cast(lineDisplay)); displayLines->InsertText(lineDocCast, 1); } } @@ -134,7 +132,7 @@ void ContractionState::DeleteLine(Sci::Line lineDoc) { if (OneToOne()) { linesInDocument--; } else { - const LINE lineDocCast = static_cast(lineDoc); + const LINE lineDocCast = line_cast(lineDoc); if (GetVisible(lineDoc)) { displayLines->InsertText(lineDocCast, -heights->ValueAt(lineDocCast)); } @@ -170,7 +168,7 @@ Sci::Line ContractionState::LinesDisplayed() const noexcept { if (OneToOne()) { return linesInDocument; } else { - return displayLines->PositionFromPartition(static_cast(LinesInDoc())); + return displayLines->PositionFromPartition(line_cast(LinesInDoc())); } } @@ -181,7 +179,7 @@ Sci::Line ContractionState::DisplayFromDoc(Sci::Line lineDoc) const noexce } else { if (lineDoc > displayLines->Partitions()) lineDoc = displayLines->Partitions(); - return displayLines->PositionFromPartition(static_cast(lineDoc)); + return displayLines->PositionFromPartition(line_cast(lineDoc)); } } @@ -199,9 +197,9 @@ Sci::Line ContractionState::DocFromDisplay(Sci::Line lineDisplay) const no return 0; } if (lineDisplay > LinesDisplayed()) { - return displayLines->PartitionFromPosition(static_cast(LinesDisplayed())); + return displayLines->PartitionFromPosition(line_cast(LinesDisplayed())); } - const Sci::Line lineDoc = displayLines->PartitionFromPosition(static_cast(lineDisplay)); + const Sci::Line lineDoc = displayLines->PartitionFromPosition(line_cast(lineDisplay)); PLATFORM_ASSERT(GetVisible(lineDoc)); return lineDoc; } @@ -210,7 +208,7 @@ Sci::Line ContractionState::DocFromDisplay(Sci::Line lineDisplay) const no template void ContractionState::InsertLines(Sci::Line lineDoc, Sci::Line lineCount) { if (OneToOne()) { - linesInDocument += static_cast(lineCount); + linesInDocument += line_cast(lineCount); } else { for (Sci::Line l = 0; l < lineCount; l++) { InsertLine(lineDoc + l); @@ -222,7 +220,7 @@ void ContractionState::InsertLines(Sci::Line lineDoc, Sci::Line lineCount) template void ContractionState::DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) { if (OneToOne()) { - linesInDocument -= static_cast(lineCount); + linesInDocument -= line_cast(lineCount); } else { for (Sci::Line l = 0; l < lineCount; l++) { DeleteLine(lineDoc); @@ -238,7 +236,7 @@ bool ContractionState::GetVisible(Sci::Line lineDoc) const noexcept { } else { if (lineDoc >= visible->Length()) return true; - return visible->ValueAt(static_cast(lineDoc)) == 1; + return visible->ValueAt(line_cast(lineDoc)) == 1; } } @@ -248,23 +246,26 @@ bool ContractionState::SetVisible(Sci::Line lineDocStart, Sci::Line lineDo return false; } else { EnsureData(); - Sci::Line delta = 0; Check(); if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < LinesInDoc())) { + bool changed = false; for (Sci::Line line = lineDocStart; line <= lineDocEnd; line++) { if (GetVisible(line) != isVisible) { - const int heightLine = heights->ValueAt(static_cast(line)); + changed = true; + const int heightLine = heights->ValueAt(line_cast(line)); const int difference = isVisible ? heightLine : -heightLine; - visible->SetValueAt(static_cast(line), isVisible ? 1 : 0); - displayLines->InsertText(static_cast(line), difference); - delta += difference; + displayLines->InsertText(line_cast(line), difference); } } + if (changed) { + visible->FillRange(line_cast(lineDocStart), isVisible ? 1 : 0, + line_cast(lineDocEnd - lineDocStart) + 1); + } + Check(); + return changed; } else { return false; } - Check(); - return delta != 0; } } @@ -304,7 +305,7 @@ bool ContractionState::GetExpanded(Sci::Line lineDoc) const noexcept { return true; } else { Check(); - return expanded->ValueAt(static_cast(lineDoc)) == 1; + return expanded->ValueAt(line_cast(lineDoc)) == 1; } } @@ -314,8 +315,8 @@ bool ContractionState::SetExpanded(Sci::Line lineDoc, bool isExpanded) { return false; } else { EnsureData(); - if (isExpanded != (expanded->ValueAt(static_cast(lineDoc)) == 1)) { - expanded->SetValueAt(static_cast(lineDoc), isExpanded ? 1 : 0); + if (isExpanded != (expanded->ValueAt(line_cast(lineDoc)) == 1)) { + expanded->SetValueAt(line_cast(lineDoc), isExpanded ? 1 : 0); Check(); return true; } else { @@ -325,16 +326,28 @@ bool ContractionState::SetExpanded(Sci::Line lineDoc, bool isExpanded) { } } +template +bool ContractionState::ExpandAll() { + if (OneToOne()) { + return false; + } else { + const LINE lines = expanded->Length(); + const bool changed = expanded->FillRange(0, 1, lines).changed; + Check(); + return changed; + } +} + template Sci::Line ContractionState::ContractedNext(Sci::Line lineDocStart) const noexcept { if (OneToOne()) { return -1; } else { Check(); - if (!expanded->ValueAt(static_cast(lineDocStart))) { + if (!expanded->ValueAt(line_cast(lineDocStart))) { return lineDocStart; } else { - const Sci::Line lineDocNextChange = expanded->EndRun(static_cast(lineDocStart)); + const Sci::Line lineDocNextChange = expanded->EndRun(line_cast(lineDocStart)); if (lineDocNextChange < LinesInDoc()) return lineDocNextChange; else @@ -348,7 +361,7 @@ int ContractionState::GetHeight(Sci::Line lineDoc) const noexcept { if (OneToOne()) { return 1; } else { - return heights->ValueAt(static_cast(lineDoc)); + return heights->ValueAt(line_cast(lineDoc)); } } @@ -362,9 +375,9 @@ bool ContractionState::SetHeight(Sci::Line lineDoc, int height) { EnsureData(); if (GetHeight(lineDoc) != height) { if (GetVisible(lineDoc)) { - displayLines->InsertText(static_cast(lineDoc), height - GetHeight(lineDoc)); + displayLines->InsertText(line_cast(lineDoc), height - GetHeight(lineDoc)); } - heights->SetValueAt(static_cast(lineDoc), height); + heights->SetValueAt(line_cast(lineDoc), height); Check(); return true; } else { @@ -378,7 +391,7 @@ bool ContractionState::SetHeight(Sci::Line lineDoc, int height) { template void ContractionState::ShowAll() noexcept { - const LINE lines = static_cast(LinesInDoc()); + const LINE lines = line_cast(LinesInDoc()); Clear(); linesInDocument = lines; } diff --git a/scintilla/src/ContractionState.h b/scintilla/src/ContractionState.h index 55c390c556..ae753f8d16 100644 --- a/scintilla/src/ContractionState.h +++ b/scintilla/src/ContractionState.h @@ -36,6 +36,7 @@ class IContractionState { virtual bool GetExpanded(Sci::Line lineDoc) const noexcept=0; virtual bool SetExpanded(Sci::Line lineDoc, bool isExpanded)=0; + virtual bool ExpandAll()=0; virtual Sci::Line ContractedNext(Sci::Line lineDocStart) const noexcept =0; virtual int GetHeight(Sci::Line lineDoc) const noexcept=0; diff --git a/scintilla/src/Decoration.cxx b/scintilla/src/Decoration.cxx index a26eecd1c0..8d901b9ece 100644 --- a/scintilla/src/Decoration.cxx +++ b/scintilla/src/Decoration.cxx @@ -34,6 +34,13 @@ namespace { template class Decoration : public IDecoration { int indicator; + + // pos_cast(): cast Sci::Position to either 32-bit or 64-bit value + // This avoids warnings from Visual C++ Code Analysis and shortens code + static constexpr POS pos_cast(Sci::Position pos) noexcept { + return static_cast(pos); + } + public: RunStyles rs; @@ -50,19 +57,19 @@ class Decoration : public IDecoration { return rs.Length(); } int ValueAt(Sci::Position position) const noexcept override { - return rs.ValueAt(static_cast(position)); + return rs.ValueAt(pos_cast(position)); } Sci::Position StartRun(Sci::Position position) const noexcept override { - return rs.StartRun(static_cast(position)); + return rs.StartRun(pos_cast(position)); } Sci::Position EndRun(Sci::Position position) const noexcept override { - return rs.EndRun(static_cast(position)); + return rs.EndRun(pos_cast(position)); } void SetValueAt(Sci::Position position, int value) override { - rs.SetValueAt(static_cast(position), value); + rs.SetValueAt(pos_cast(position), value); } void InsertSpace(Sci::Position position, Sci::Position insertLength) override { - rs.InsertSpace(static_cast(position), static_cast(insertLength)); + rs.InsertSpace(pos_cast(position), pos_cast(insertLength)); } Sci::Position Runs() const noexcept override { return rs.Runs(); @@ -85,6 +92,13 @@ class DecorationList : public IDecorationList { void Delete(int indicator); void DeleteAnyEmpty(); void SetView(); + + // pos_cast(): cast Sci::Position to either 32-bit or 64-bit value + // This avoids warnings from Visual C++ Code Analysis and shortens code + static constexpr POS pos_cast(Sci::Position pos) noexcept { + return static_cast(pos); + } + public: DecorationList(); @@ -96,7 +110,7 @@ class DecorationList : public IDecorationList { void SetCurrentIndicator(int indicator) override; int GetCurrentIndicator() const noexcept override { return currentIndicator; } - void SetCurrentValue(int value) override; + void SetCurrentValue(int value) noexcept override; int GetCurrentValue() const noexcept override { return currentValue; } // Returns changed=true if some values may have changed @@ -139,7 +153,7 @@ template Decoration *DecorationList::Create(int indicator, Sci::Position length) { currentIndicator = indicator; std::unique_ptr> decoNew = std::make_unique>(indicator); - decoNew->rs.InsertSpace(0, static_cast(length)); + decoNew->rs.InsertSpace(0, pos_cast(length)); typename std::vector>>::iterator it = std::lower_bound( decorationList.begin(), decorationList.end(), decoNew, @@ -172,7 +186,7 @@ void DecorationList::SetCurrentIndicator(int indicator) { } template -void DecorationList::SetCurrentValue(int value) { +void DecorationList::SetCurrentValue(int value) noexcept { currentValue = value ? value : 1; } @@ -185,9 +199,9 @@ FillResult DecorationList::FillRange(Sci::Position position, } } // Converting result from POS to Sci::Position as callers not polymorphic. - const FillResult frInPOS = current->rs.FillRange(static_cast(position), value, static_cast(fillLength)); + const FillResult frInPOS = current->rs.FillRange(pos_cast(position), value, pos_cast(fillLength)); const FillResult fr { frInPOS.changed, frInPOS.position, frInPOS.fillLength }; - if (current->Empty()) { + if (current->Empty()) { Delete(currentIndicator); } return fr; @@ -198,9 +212,9 @@ void DecorationList::InsertSpace(Sci::Position position, Sci::Position inse const bool atEnd = position == lengthDocument; lengthDocument += insertLength; for (const std::unique_ptr> &deco : decorationList) { - deco->rs.InsertSpace(static_cast(position), static_cast(insertLength)); + deco->rs.InsertSpace(pos_cast(position), pos_cast(insertLength)); if (atEnd) { - deco->rs.FillRange(static_cast(position), 0, static_cast(insertLength)); + deco->rs.FillRange(pos_cast(position), 0, pos_cast(insertLength)); } } } @@ -209,7 +223,7 @@ template void DecorationList::DeleteRange(Sci::Position position, Sci::Position deleteLength) { lengthDocument -= deleteLength; for (const std::unique_ptr> &deco : decorationList) { - deco->rs.DeleteRange(static_cast(position), static_cast(deleteLength)); + deco->rs.DeleteRange(pos_cast(position), pos_cast(deleteLength)); } DeleteAnyEmpty(); if (decorationList.size() != decorationView.size()) { @@ -253,7 +267,7 @@ template int DecorationList::AllOnFor(Sci::Position position) const noexcept { int mask = 0; for (const std::unique_ptr> &deco : decorationList) { - if (deco->rs.ValueAt(static_cast(position))) { + if (deco->rs.ValueAt(pos_cast(position))) { if (deco->Indicator() < static_cast(Scintilla::IndicatorNumbers::Ime)) { mask |= 1u << deco->Indicator(); } @@ -266,7 +280,7 @@ template int DecorationList::ValueAt(int indicator, Sci::Position position) noexcept { const Decoration *deco = DecorationFromIndicator(indicator); if (deco) { - return deco->rs.ValueAt(static_cast(position)); + return deco->rs.ValueAt(pos_cast(position)); } return 0; } @@ -275,7 +289,7 @@ template Sci::Position DecorationList::Start(int indicator, Sci::Position position) noexcept { const Decoration *deco = DecorationFromIndicator(indicator); if (deco) { - return deco->rs.StartRun(static_cast(position)); + return deco->rs.StartRun(pos_cast(position)); } return 0; } @@ -284,7 +298,7 @@ template Sci::Position DecorationList::End(int indicator, Sci::Position position) noexcept { const Decoration *deco = DecorationFromIndicator(indicator); if (deco) { - return deco->rs.EndRun(static_cast(position)); + return deco->rs.EndRun(pos_cast(position)); } return 0; } diff --git a/scintilla/src/Decoration.h b/scintilla/src/Decoration.h index c8faafec92..9c47d6c082 100644 --- a/scintilla/src/Decoration.h +++ b/scintilla/src/Decoration.h @@ -32,7 +32,7 @@ class IDecorationList { virtual void SetCurrentIndicator(int indicator) = 0; virtual int GetCurrentIndicator() const noexcept = 0; - virtual void SetCurrentValue(int value) = 0; + virtual void SetCurrentValue(int value) noexcept = 0; virtual int GetCurrentValue() const noexcept = 0; // Returns with changed=true if some values may have changed diff --git a/scintilla/src/Document.cxx b/scintilla/src/Document.cxx index 3d6e48a98d..ada7f44366 100644 --- a/scintilla/src/Document.cxx +++ b/scintilla/src/Document.cxx @@ -58,7 +58,6 @@ LexInterface::~LexInterface() noexcept = default; void LexInterface::SetInstance(ILexer5 *instance_) { instance.reset(instance_); - pdoc->LexerChanged(); } void LexInterface::Colourise(Sci::Position start, Sci::Position end) { @@ -339,8 +338,32 @@ void Document::TentativeUndo() { } } -int Document::GetMark(Sci::Line line) const noexcept { - return Markers()->MarkValue(line); +int Document::GetMark(Sci::Line line, bool includeChangeHistory) const { + int marksHistory = 0; + if (includeChangeHistory && (line < LinesTotal())) { + int marksEdition = 0; + + const Sci::Position start = LineStart(line); + const Sci::Position lineNext = LineStart(line + 1); + for (Sci::Position position = start; position < lineNext;) { + const int edition = EditionAt(position); + if (edition) { + marksEdition |= 1 << (edition-1); + } + position = EditionEndRun(position); + } + const Sci::Position lineEnd = LineEnd(line); + for (Sci::Position position = start; position <= lineEnd;) { + marksEdition |= EditionDeletesAt(position); + position = EditionNextDelete(position); + } + + /* Bits: RevertedToOrigin, Saved, Modified, RevertedToModified */ + constexpr unsigned int editionShift = static_cast(MarkerOutline::HistoryRevertedToOrigin); + marksHistory = marksEdition << editionShift; + } + + return marksHistory | Markers()->MarkValue(line); } Sci::Line Document::MarkerNext(Sci::Line lineStart, int mask) const noexcept { @@ -348,7 +371,7 @@ Sci::Line Document::MarkerNext(Sci::Line lineStart, int mask) const noexcept { } int Document::AddMark(Sci::Line line, int markerNum) { - if (line >= 0 && line <= LinesTotal()) { + if (line >= 0 && line < LinesTotal()) { const int prev = Markers()->AddMark(line, markerNum, LinesTotal()); const DocModification mh(ModificationFlags::ChangeMarker, LineStart(line), 0, 0, nullptr, line); NotifyModified(mh); @@ -359,7 +382,7 @@ int Document::AddMark(Sci::Line line, int markerNum) { } void Document::AddMarkSet(Sci::Line line, int valueSet) { - if (line < 0 || line > LinesTotal()) { + if (line < 0 || line >= LinesTotal()) { return; } unsigned int m = valueSet; @@ -1576,6 +1599,9 @@ Sci::Position Document::GetColumn(Sci::Position pos) { return column; } else if (i >= Length()) { return column; + } else if (UTF8IsAscii(ch)) { + column++; + i++; } else { column++; i = NextPosition(i, 1); @@ -2405,13 +2431,6 @@ void Document::StyleToAdjustingLineDuration(Sci::Position pos) { durationStyleOneByte.AddSample(pos - stylingStart, epStyling.Duration()); } -void Document::LexerChanged() { - // Tell the watchers the lexer has changed. - for (const WatcherWithUserData &watcher : watchers) { - watcher.watcher->NotifyLexerChanged(this, watcher.userData); - } -} - LexInterface *Document::GetLexInterface() const noexcept { return pli.get(); } @@ -2421,7 +2440,7 @@ void Document::SetLexInterface(std::unique_ptr pLexInterface) noex } int SCI_METHOD Document::SetLineState(Sci_Position line, int state) { - const int statePrevious = States()->SetLineState(line, state); + const int statePrevious = States()->SetLineState(line, state, LinesTotal()); if (state != statePrevious) { const DocModification mh(ModificationFlags::ChangeLineState, LineStart(line), 0, 0, nullptr, static_cast(line)); @@ -2515,6 +2534,9 @@ int Document::AnnotationLines(Sci::Line line) const noexcept { } void Document::AnnotationClearAll() { + if (Annotations()->Empty()) { + return; + } const Sci::Line maxEditorLine = LinesTotal(); for (Sci::Line l=0; lEmpty()) { + return; + } const Sci::Line maxEditorLine = LinesTotal(); for (Sci::Line l=0; ldbcsCodePage, BidirectionalR2L()); +} + void EditModel::SetDefaultFoldDisplayText(const char *text) { defaultFoldDisplayText = IsNullOrEmpty(text) ? UniqueString() : UniqueStringCopy(text); } @@ -122,3 +126,7 @@ InSelection EditModel::LineEndInSelection(Sci::Line lineDoc) const { const Sci::Position posAfterLineEnd = pdoc->LineStart(lineDoc + 1); return sel.InSelectionForEOL(posAfterLineEnd); } + +int EditModel::GetMark(Sci::Line line) const { + return pdoc->GetMark(line, FlagSet(changeHistoryOption, ChangeHistoryOption::Markers)); +} diff --git a/scintilla/src/EditModel.h b/scintilla/src/EditModel.h index 0887b86ca0..f62c28b1fa 100644 --- a/scintilla/src/EditModel.h +++ b/scintilla/src/EditModel.h @@ -49,6 +49,8 @@ class EditModel { bool hotspotSingleLine; Sci::Position hoverIndicatorPos; + Scintilla::ChangeHistoryOption changeHistoryOption = Scintilla::ChangeHistoryOption::Disabled; + // Wrapping support int wrapWidth; @@ -61,15 +63,17 @@ class EditModel { EditModel &operator=(const EditModel &) = delete; EditModel &operator=(EditModel &&) = delete; virtual ~EditModel(); - virtual Sci::Line TopLineOfMain() const = 0; + virtual Sci::Line TopLineOfMain() const noexcept = 0; virtual Point GetVisibleOriginInMain() const = 0; virtual Sci::Line LinesOnScreen() const = 0; bool BidirectionalEnabled() const noexcept; bool BidirectionalR2L() const noexcept; + SurfaceMode CurrentSurfaceMode() const noexcept; void SetDefaultFoldDisplayText(const char *text); const char *GetDefaultFoldDisplayText() const noexcept; const char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept; InSelection LineEndInSelection(Sci::Line lineDoc) const; + [[nodiscard]] int GetMark(Sci::Line line) const; }; } diff --git a/scintilla/src/EditView.cxx b/scintilla/src/EditView.cxx index 444fb3283f..100d3d18bd 100644 --- a/scintilla/src/EditView.cxx +++ b/scintilla/src/EditView.cxx @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include "ScintillaTypes.h" #include "ScintillaMessages.h" @@ -182,7 +185,6 @@ void Hexits(char *hexits, int ch) noexcept { EditView::EditView() { tabWidthMinimumPixels = 2; // needed for calculating tab stops for fractional proportional fonts - hideSelection = false; drawOverstrikeCaret = true; bufferedDraw = true; phasesDraw = PhasesDraw::Two; @@ -191,7 +193,9 @@ EditView::EditView() { additionalCaretsVisible = true; imeCaretBlockOverride = false; llc.SetLevel(LineCache::Caret); - posCache.SetSize(0x400); + posCache = CreatePositionCache(); + posCache->SetSize(0x400); + maxLayoutThreads = 1; tabArrowHeight = 4; customDrawTabArrow = nullptr; customDrawWrapMarker = nullptr; @@ -217,6 +221,14 @@ bool EditView::LinesOverlap() const noexcept { return phasesDraw == PhasesDraw::Multiple; } +void EditView::SetLayoutThreads(unsigned int threads) noexcept { + maxLayoutThreads = std::clamp(threads, 1U, std::thread::hardware_concurrency()); +} + +unsigned int EditView::GetLayoutThreads() const noexcept { + return maxLayoutThreads; +} + void EditView::ClearAllTabstops() noexcept { ldTabstops.reset(); } @@ -372,6 +384,61 @@ bool ViewIsASCII(std::string_view text) { return std::all_of(text.cbegin(), text.cend(), IsASCII); } +void LayoutSegments(IPositionCache *pCache, + Surface *surface, + const ViewStyle &vstyle, + LineLayout *ll, + const std::vector &segments, + std::atomic &nextIndex, + const bool textUnicode, + const bool multiThreaded) { + while (true) { + const uint32_t i = nextIndex.fetch_add(1, std::memory_order_acq_rel); + if (i >= segments.size()) { + break; + } + const TextSegment &ts = segments[i]; + if (vstyle.styles[ll->styles[ts.start]].visible) { + if (ts.representation) { + XYPOSITION representationWidth = vstyle.controlCharWidth; + if (ll->chars[ts.start] == '\t') { + // Tab is a special case of representation, taking a variable amount of space + // which will be filled in later. + representationWidth = 0; + } else { + if (representationWidth <= 0.0) { + assert(ts.representation->stringRep.length() <= Representation::maxLength); + XYPOSITION positionsRepr[Representation::maxLength + 1]; + // ts.representation->stringRep is UTF-8 which only matches cache if document is UTF-8 + // or it only contains ASCII which is a subset of all currently supported encodings. + if (textUnicode || ViewIsASCII(ts.representation->stringRep)) { + pCache->MeasureWidths(surface, vstyle, StyleControlChar, ts.representation->stringRep, + positionsRepr, multiThreaded); + } else { + surface->MeasureWidthsUTF8(vstyle.styles[StyleControlChar].font.get(), ts.representation->stringRep, positionsRepr); + } + representationWidth = positionsRepr[ts.representation->stringRep.length() - 1]; + if (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) { + representationWidth += vstyle.ctrlCharPadding; + } + } + } + for (int ii = 0; ii < ts.length; ii++) { + ll->positions[ts.start + 1 + ii] = representationWidth; + } + } else { + if ((ts.length == 1) && (' ' == ll->chars[ts.start])) { + // Over half the segments are single characters and of these about half are space characters. + ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth; + } else { + pCache->MeasureWidths(surface, vstyle, ll->styles[ts.start], + std::string_view(&ll->chars[ts.start], ts.length), &ll->positions[ts.start + 1], multiThreaded); + } + } + } + } +} + } /** @@ -382,7 +449,6 @@ bool ViewIsASCII(std::string_view text) { void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) { if (!ll) return; - const Sci::Line line = ll->LineNumber(); PLATFORM_ASSERT(line < model.pdoc->LinesTotal()); PLATFORM_ASSERT(ll->chars); @@ -464,56 +530,71 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt ll->positions[0] = 0; bool lastSegItalics = false; + std::vector segments; BreakFinder bfLayout(ll, nullptr, Range(0, numCharsInLine), posLineStart, 0, BreakFinder::BreakFor::Text, model.pdoc, &model.reprs, nullptr); while (bfLayout.More()) { + segments.push_back(bfLayout.Next()); + } - const TextSegment ts = bfLayout.Next(); + ll->ClearPositions(); - std::fill(&ll->positions[ts.start + 1], &ll->positions[ts.end() + 1], 0.0f); - if (vstyle.styles[ll->styles[ts.start]].visible) { - if (ts.representation) { - XYPOSITION representationWidth = vstyle.controlCharWidth; - if (ll->chars[ts.start] == '\t') { - // Tab is a special case of representation, taking a variable amount of space - const XYPOSITION x = ll->positions[ts.start]; - representationWidth = NextTabstopPos(line, x, vstyle.tabWidth) - ll->positions[ts.start]; - } else { - if (representationWidth <= 0.0) { - assert(ts.representation->stringRep.length() <= Representation::maxLength); - XYPOSITION positionsRepr[Representation::maxLength+1]; - // ts.representation->stringRep is UTF-8 which only matches cache if document is UTF-8 - // or it only contains ASCII which is a subset of all currently supported encodings. - if ((CpUtf8 == model.pdoc->dbcsCodePage) || ViewIsASCII(ts.representation->stringRep)) { - posCache.MeasureWidths(surface, vstyle, StyleControlChar, ts.representation->stringRep, - positionsRepr); - } else { - surface->MeasureWidthsUTF8(vstyle.styles[StyleControlChar].font.get(), ts.representation->stringRep, positionsRepr); - } - representationWidth = positionsRepr[ts.representation->stringRep.length() - 1]; - if (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) { - representationWidth += vstyle.ctrlCharPadding; - } - } - } - for (int ii = 0; ii < ts.length; ii++) - ll->positions[ts.start + 1 + ii] = representationWidth; - } else { - if ((ts.length == 1) && (' ' == ll->chars[ts.start])) { - // Over half the segments are single characters and of these about half are space characters. - ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth; - } else { - posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], - std::string_view(&ll->chars[ts.start], ts.length), &ll->positions[ts.start + 1]); - } - } - lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic); + if (!segments.empty()) { + + const size_t threadsForLength = std::max(1, numCharsInLine / bytesPerLayoutThread); + size_t threads = std::min({ segments.size(), threadsForLength, maxLayoutThreads }); + if (!surface->SupportsFeature(Supports::ThreadSafeMeasureWidths)) { + threads = 1; } - for (Sci::Position posToIncrease = ts.start + 1; posToIncrease <= ts.end(); posToIncrease++) { - ll->positions[posToIncrease] += ll->positions[ts.start]; + std::atomic nextIndex = 0; + + const bool textUnicode = CpUtf8 == model.pdoc->dbcsCodePage; + const bool multiThreaded = threads > 1; + IPositionCache *pCache = posCache.get(); + + // If only 1 thread needed then use the main thread, else spin up multiple + const std::launch policy = (multiThreaded) ? std::launch::async : std::launch::deferred; + + std::vector> futures; + for (size_t th = 0; th < threads; th++) { + // Find relative positions of everything except for tabs + std::future fut = std::async(policy, + [pCache, surface, &vstyle, &ll, &segments, &nextIndex, textUnicode, multiThreaded]() { + LayoutSegments(pCache, surface, vstyle, ll, segments, nextIndex, textUnicode, multiThreaded); + }); + futures.push_back(std::move(fut)); + } + for (const std::future &f : futures) { + f.wait(); } } + // Accumulate absolute positions from relative positions within segments and expand tabs + XYPOSITION xPosition = 0.0; + size_t iByte = 0; + ll->positions[iByte++] = xPosition; + for (const TextSegment &ts : segments) { + if (vstyle.styles[ll->styles[ts.start]].visible && + ts.representation && + (ll->chars[ts.start] == '\t')) { + // Simple visible tab, go to next tab stop + const XYPOSITION startTab = ll->positions[ts.start]; + const XYPOSITION nextTab = NextTabstopPos(line, startTab, vstyle.tabWidth); + xPosition += nextTab - startTab; + } + const XYPOSITION xBeginSegment = xPosition; + for (int i = 0; i < ts.length; i++) { + xPosition = ll->positions[iByte] + xBeginSegment; + ll->positions[iByte++] = xPosition; + } + } + + if (!segments.empty()) { + // Not quite the same as before which would effectively ignore trailing invisible segments + const TextSegment &ts = segments.back(); + lastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic); + } + // Small hack to make lines that end with italics not cut off the edge of the last character if (lastSegItalics) { ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset; @@ -606,8 +687,7 @@ void EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewSt } } lastLineStart = lastGoodBreak; - ll->lines++; - ll->SetLineStart(ll->lines, static_cast(lastLineStart)); + ll->AddLineStart(lastLineStart); startOffset = ll->positions[lastLineStart]; // take into account the space for start wrap mark and indent startOffset += width - ll->wrapIndent; @@ -631,7 +711,7 @@ void EditView::UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, L ll->bidiData->stylesFonts[ll->numCharsInLine].reset(); for (int charsInLine = 0; charsInLine < ll->numCharsInLine; charsInLine++) { - const int charWidth = UTF8DrawBytes(reinterpret_cast(&ll->chars[charsInLine]), ll->numCharsInLine - charsInLine); + const int charWidth = UTF8DrawBytes(&ll->chars[charsInLine], ll->numCharsInLine - charsInLine); const Representation *repr = model.reprs.RepresentationFromCharacter(std::string_view(&ll->chars[charsInLine], charWidth)); ll->bidiData->widthReprs[charsInLine] = 0.0f; @@ -692,8 +772,8 @@ Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, S } } pt.y += (lineVisible - topLine) * vs.lineHeight; + pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth; } - pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth; return pt; } @@ -852,7 +932,7 @@ Sci::Position EditView::StartEndDisplayLine(Surface *surface, const EditModel &m namespace { -constexpr ColourRGBA bugColour = ColourRGBA(0xff, 0, 0xff, 0xf0); +constexpr ColourRGBA bugColour = ColourRGBA(0xff, 0, 0xfe, 0xf0); // Selection background colours are always defined, the value_or is to show if bug @@ -994,7 +1074,7 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle rcSegment.right = xEol + xStart + virtualSpace; const ColourRGBA backgroundFill = background.value_or(vsDraw.styles[ll->styles[ll->numCharsInLine]].back); surface->FillRectangleAligned(rcSegment, backgroundFill); - if (!hideSelection && (vsDraw.selection.layer == Layer::Base)) { + if (vsDraw.selection.visible && (vsDraw.selection.layer == Layer::Base)) { const SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)), SelectionPosition(model.pdoc->LineEnd(line), model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)))); @@ -1016,7 +1096,7 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle } InSelection eolInSelection = InSelection::inNone; - if (!hideSelection && lastSubLine) { + if (vsDraw.selection.visible && lastSubLine) { eolInSelection = model.LineEndInSelection(line); } @@ -1158,14 +1238,13 @@ static void DrawIndicator(int indicNum, Sci::Position startPos, Sci::Position en int value, bool bidiEnabled, int tabWidthMinimumPixels) { const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)]; + const XYPOSITION horizontalOffset = xStart - subLineStart; std::vector rectangles; - const PRectangle rcIndic( - ll->positions[startPos] + xStart - subLineStart, - rcLine.top + vsDraw.maxAscent, - ll->positions[endPos] + xStart - subLineStart, - rcLine.top + vsDraw.maxAscent + 3); + const XYPOSITION left = ll->XInLine(startPos) + horizontalOffset; + const XYPOSITION right = ll->XInLine(endPos) + horizontalOffset; + const PRectangle rcIndic(left, rcLine.top + vsDraw.maxAscent, right, rcLine.top + vsDraw.maxAscent + 3); if (bidiEnabled) { ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right - xStart, tabWidthMinimumPixels); @@ -1189,7 +1268,7 @@ static void DrawIndicator(int indicNum, Sci::Position startPos, Sci::Position en // Allow full descent space for character indicators rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent; if (secondCharacter >= 0) { - rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart; + rcFirstCharacter.right = ll->XInLine(secondCharacter) + horizontalOffset; } else { // Indicator continued from earlier line so make an empty box and don't draw rcFirstCharacter.right = rcFirstCharacter.left; @@ -1208,24 +1287,20 @@ static void DrawIndicators(Surface *surface, const EditModel &model, const ViewS for (const IDecoration *deco : model.pdoc->decorations->View()) { if (under == vsDraw.indicators[deco->Indicator()].under) { Sci::Position startPos = posLineStart + lineStart; - if (!deco->ValueAt(startPos)) { - startPos = deco->EndRun(startPos); - } - while ((startPos < posLineEnd) && (deco->ValueAt(startPos))) { + while (startPos < posLineEnd) { const Range rangeRun(deco->StartRun(startPos), deco->EndRun(startPos)); const Sci::Position endPos = std::min(rangeRun.end, posLineEnd); - const bool hover = vsDraw.indicators[deco->Indicator()].IsDynamic() && - rangeRun.ContainsCharacter(model.hoverIndicatorPos); const int value = deco->ValueAt(startPos); - const Indicator::State state = hover ? Indicator::State::hover : Indicator::State::normal; - const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1); - DrawIndicator(deco->Indicator(), startPos - posLineStart, endPos - posLineStart, - surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, state, - value, model.BidirectionalEnabled(), tabWidthMinimumPixels); - startPos = endPos; - if (!deco->ValueAt(startPos)) { - startPos = deco->EndRun(startPos); + if (value) { + const bool hover = vsDraw.indicators[deco->Indicator()].IsDynamic() && + rangeRun.ContainsCharacter(model.hoverIndicatorPos); + const Indicator::State state = hover ? Indicator::State::hover : Indicator::State::normal; + const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1); + DrawIndicator(deco->Indicator(), startPos - posLineStart, endPos - posLineStart, + surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, state, + value, model.BidirectionalEnabled(), tabWidthMinimumPixels); } + startPos = endPos; } } } @@ -1236,21 +1311,55 @@ static void DrawIndicators(Surface *surface, const EditModel &model, const ViewS const int braceIndicator = (model.bracesMatchStyle == StyleBraceLight) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator; if (under == vsDraw.indicators[braceIndicator].under) { const Range rangeLine(posLineStart + lineStart, posLineEnd); - if (rangeLine.ContainsCharacter(model.braces[0])) { - const Sci::Position braceOffset = model.braces[0] - posLineStart; - if (braceOffset < ll->numCharsInLine) { - const Sci::Position secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[0] + 1, 1) - posLineStart; - DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, - subLine, Indicator::State::normal, 1, model.BidirectionalEnabled(), tabWidthMinimumPixels); + for (size_t brace = 0; brace <= 1; brace++) { + if (rangeLine.ContainsCharacter(model.braces[brace])) { + const Sci::Position braceOffset = model.braces[brace] - posLineStart; + if (braceOffset < ll->numCharsInLine) { + const Sci::Position braceEnd = model.pdoc->MovePositionOutsideChar(model.braces[brace] + 1, 1) - posLineStart; + DrawIndicator(braceIndicator, braceOffset, braceEnd, + surface, vsDraw, ll, xStart, rcLine, braceEnd, subLine, Indicator::State::normal, + 1, model.BidirectionalEnabled(), tabWidthMinimumPixels); + } + } + } + } + } + + if (FlagSet(model.changeHistoryOption, ChangeHistoryOption::Indicators)) { + // Draw editions + constexpr int indexHistory = static_cast(IndicatorNumbers::HistoryRevertedToOriginInsertion); + { + // Draw insertions + Sci::Position startPos = posLineStart + lineStart; + while (startPos < posLineEnd) { + const Range rangeRun(startPos, model.pdoc->EditionEndRun(startPos)); + const Sci::Position endPos = std::min(rangeRun.end, posLineEnd); + const int edition = model.pdoc->EditionAt(startPos); + if (edition != 0) { + const int indicator = (edition - 1) * 2 + indexHistory; + const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1); + DrawIndicator(indicator, startPos - posLineStart, endPos - posLineStart, + surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, Indicator::State::normal, + 1, model.BidirectionalEnabled(), tabWidthMinimumPixels); } + startPos = endPos; } - if (rangeLine.ContainsCharacter(model.braces[1])) { - const Sci::Position braceOffset = model.braces[1] - posLineStart; - if (braceOffset < ll->numCharsInLine) { - const Sci::Position secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[1] + 1, 1) - posLineStart; - DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, - subLine, Indicator::State::normal, 1, model.BidirectionalEnabled(), tabWidthMinimumPixels); + } + { + // Draw deletions + Sci::Position startPos = posLineStart + lineStart; + while (startPos <= posLineEnd) { + const unsigned int editions = model.pdoc->EditionDeletesAt(startPos); + const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(startPos + 1, 1); + for (unsigned int edition=0; edition<4; edition++) { + if (editions & (1 << edition)) { + const int indicator = edition * 2 + indexHistory + 1; + DrawIndicator(indicator, startPos - posLineStart, posSecond - posLineStart, + surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, Indicator::State::normal, + 1, model.BidirectionalEnabled(), tabWidthMinimumPixels); + } } + startPos = model.pdoc->EditionNextDelete(startPos); } } } @@ -1272,7 +1381,7 @@ void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, con const int widthFoldDisplayText = static_cast(surface->WidthText(fontText, foldDisplayText)); InSelection eolInSelection = InSelection::inNone; - if (!hideSelection) { + if (vsDraw.selection.visible) { eolInSelection = model.LineEndInSelection(line); } @@ -1282,7 +1391,7 @@ void EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, con rcSegment.left = xStart + static_cast(ll->positions[ll->numCharsInLine] - subLineStart) + virtualSpace + vsDraw.aveCharWidth; rcSegment.right = rcSegment.left + static_cast(widthFoldDisplayText); - const std::optional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + const std::optional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret); const std::optional selectionFore = SelectionForeground(model, vsDraw, eolInSelection); const ColourRGBA textFore = selectionFore.value_or(vsDraw.styles[StyleFoldDisplayText].fore); const ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, @@ -1403,11 +1512,13 @@ void EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, c const char *textFoldDisplay = model.GetFoldDisplayText(line); if (textFoldDisplay) { const std::string_view foldDisplayText(textFoldDisplay); - rcSegment.left += (static_cast(surface->WidthText(fontText, foldDisplayText)) + vsDraw.aveCharWidth); + rcSegment.left += static_cast( + surface->WidthText(vsDraw.styles[StyleFoldDisplayText].font.get(), foldDisplayText)) + + vsDraw.aveCharWidth; } rcSegment.right = rcSegment.left + static_cast(widthEOLAnnotationText); - const std::optional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + const std::optional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret); const ColourRGBA textFore = vsDraw.styles[style].fore; const ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, InSelection::inNone, false, static_cast(style), -1); @@ -1527,6 +1638,11 @@ void EditView::DrawAnnotation(Surface *surface, const EditModel &model, const Vi surface->FillRectangle(Side(rcBorder, Edge::bottom, 1), colourBorder); } } + } else { + // No annotation to draw so show bug with bugColour + if (FlagSet(phase, DrawPhase::back)) { + surface->FillRectangle(rcSegment, bugColour.Opaque()); + } } } @@ -1600,7 +1716,7 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt Sci::Line lineDoc, int xStart, PRectangle rcLine, int subLine) const { // When drag is active it is the only caret drawn const bool drawDrag = model.posDrag.IsValid(); - if (hideSelection && !drawDrag) + if (!vsDraw.selection.visible && !drawDrag) return; const Sci::Position posLineStart = model.pdoc->LineStart(lineDoc); // For each selection draw @@ -1703,7 +1819,9 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt } } -static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, +namespace { + +void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll, int xStart, PRectangle rcLine, std::optional background, DrawWrapMarkerFn customDrawWrapMarker, bool caretActive) { // default bgnd here.. @@ -1743,7 +1861,7 @@ static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, c // such that, if the caret is inside the main selection, the beginning or end of that selection // is at the end of a text segment. // This function should only be called if iDoc is within the main selection. -static InSelection CharacterInCursesSelection(Sci::Position iDoc, const EditModel &model, const ViewStyle &vsDraw) { +InSelection CharacterInCursesSelection(Sci::Position iDoc, const EditModel &model, const ViewStyle &vsDraw) noexcept { const SelectionPosition &posCaret = model.sel.RangeMain().caret; const bool caretAtStart = posCaret < model.sel.RangeMain().anchor && posCaret.Position() == iDoc; const bool caretAtEnd = posCaret > model.sel.RangeMain().anchor && @@ -1752,6 +1870,8 @@ static InSelection CharacterInCursesSelection(Sci::Position iDoc, const EditMode return (caretAtStart || caretAtEnd) ? InSelection::inNone : InSelection::inMain; } +} + void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine, Range lineRange, Sci::Position posLineStart, int xStart, int subLine, std::optional background) const { @@ -1786,7 +1906,7 @@ void EditView::DrawBackground(Surface *surface, const EditModel &model, const Vi if (rcSegment.right > rcLine.right) rcSegment.right = rcLine.right; - InSelection inSelection = hideSelection ? InSelection::inNone : model.sel.CharacterInSelection(iDoc); + InSelection inSelection = vsDraw.selection.visible ? model.sel.CharacterInSelection(iDoc) : InSelection::inNone; if (FlagSet(vsDraw.caret.style, CaretStyle::Curses) && (inSelection == InSelection::inMain)) inSelection = CharacterInCursesSelection(iDoc, model, vsDraw); const bool inHotspot = model.hotspot.Valid() && model.hotspot.ContainsCharacter(iDoc); @@ -1858,7 +1978,7 @@ static void DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLa // Draw underline mark as part of background if on base layer static void DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, Sci::Line line, PRectangle rcLine) { - int marks = model.pdoc->GetMark(line); + int marks = model.GetMark(line); for (int markBit = 0; (markBit < 32) && marks; markBit++) { if ((marks & 1) && (vsDraw.markers[markBit].markType == MarkerSymbol::Underline) && (vsDraw.markers[markBit].layer == Layer::Base)) { @@ -1890,18 +2010,20 @@ static void DrawTranslucentSelection(Surface *surface, const EditModel &model, c model, vsDraw, model.sel.RangeType(r)); const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; if (model.BidirectionalEnabled()) { - const int selectionStart = static_cast(portion.start.Position() - posLineStart - lineRange.start); - const int selectionEnd = static_cast(portion.end.Position() - posLineStart - lineRange.start); + const Sci::Position selectionStart = portion.start.Position() - posLineStart - lineRange.start; + const Sci::Position selectionEnd = portion.end.Position() - posLineStart - lineRange.start; const ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right, tabWidthMinimumPixels); std::unique_ptr slLayout = surface->Layout(&screenLine); - const std::vector intervals = slLayout->FindRangeIntervals(selectionStart, selectionEnd); - for (const Interval &interval : intervals) { - const XYPOSITION rcRight = interval.right + xStart; - const XYPOSITION rcLeft = interval.left + xStart; - const PRectangle rcSelection(rcLeft, rcLine.top, rcRight, rcLine.bottom); - surface->FillRectangleAligned(rcSelection, selectionBack); + if (slLayout) { + const std::vector intervals = slLayout->FindRangeIntervals(selectionStart, selectionEnd); + for (const Interval &interval : intervals) { + const XYPOSITION rcRight = interval.right + xStart; + const XYPOSITION rcLeft = interval.left + xStart; + const PRectangle rcSelection(rcLeft, rcLine.top, rcRight, rcLine.bottom); + surface->FillRectangleAligned(rcSelection, selectionBack); + } } if (portion.end.VirtualSpace()) { @@ -1943,7 +2065,7 @@ static void DrawTranslucentLineState(Surface *surface, const EditModel &model, c surface->FillRectangleAligned(rcLine, *vsDraw.ElementColour(Element::CaretLineBack)); } } - const int marksOfLine = model.pdoc->GetMark(line); + const int marksOfLine = model.GetMark(line); int marksDrawnInText = marksOfLine & vsDraw.maskDrawInText; for (int markBit = 0; (markBit < 32) && marksDrawnInText; markBit++) { if ((marksDrawnInText & 1) && (vsDraw.markers[markBit].layer == layer)) { @@ -2033,7 +2155,7 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi } } } - InSelection inSelection = hideSelection ? InSelection::inNone : model.sel.CharacterInSelection(iDoc); + InSelection inSelection = vsDraw.selection.visible ? model.sel.CharacterInSelection(iDoc) : InSelection::inNone; if (FlagSet(vsDraw.caret.style, CaretStyle::Curses) && (inSelection == InSelection::inMain)) inSelection = CharacterInCursesSelection(iDoc, model, vsDraw); const std::optional selectionFore = SelectionForeground(model, vsDraw, inSelection); @@ -2233,7 +2355,7 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl } // See if something overrides the line background colour. - const std::optional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + const std::optional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret); const Sci::Position posLineStart = model.pdoc->LineStart(line); @@ -2271,7 +2393,9 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl } if (FlagSet(phase, DrawPhase::text)) { - DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart, tabWidthMinimumPixels, Layer::UnderText); + if (vsDraw.selection.visible) { + DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart, tabWidthMinimumPixels, Layer::UnderText); + } DrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine, subLine, Layer::UnderText); DrawForeground(surface, model, vsDraw, ll, lineVisible, rcLine, lineRange, posLineStart, xStart, subLine, background); @@ -2298,7 +2422,7 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl DrawMarkUnderline(surface, model, vsDraw, line, rcLine); } - if (!hideSelection && FlagSet(phase, DrawPhase::selectionTranslucent)) { + if (vsDraw.selection.visible && FlagSet(phase, DrawPhase::selectionTranslucent)) { DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart, tabWidthMinimumPixels, Layer::OverText); } @@ -2359,7 +2483,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan surface = pixmapLine.get(); PLATFORM_ASSERT(pixmapLine->Initialised()); } - surface->SetMode(SurfaceMode(model.pdoc->dbcsCodePage, model.BidirectionalR2L())); + surface->SetMode(model.CurrentSurfaceMode()); const Point ptOrigin = model.GetVisibleOriginInMain(); @@ -2435,7 +2559,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan durLayout += ep.Duration(true); #endif if (ll) { - ll->containsCaret = !hideSelection && (lineDoc == lineCaret) + ll->containsCaret = vsDraw.selection.visible && (lineDoc == lineCaret) && (ll->lines == 1 || !vsDraw.caretLine.subLine || ll->InLine(caretOffset, subLine)); PRectangle rcLine = rcTextArea; @@ -2545,11 +2669,11 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan void EditView::FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line, PRectangle rcArea, int subLine) const { InSelection eolInSelection = InSelection::inNone; - if ((!hideSelection) && (subLine == (ll->lines - 1))) { + if (vsDraw.selection.visible && (subLine == (ll->lines - 1))) { eolInSelection = model.LineEndInSelection(line); } - const std::optional background = vsDraw.Background(model.pdoc->GetMark(line), model.caret.active, ll->containsCaret); + const std::optional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret); if (eolInSelection && vsDraw.selection.eolFilled && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer == Layer::Base)) { surface->FillRectangleAligned(rcArea, Fill(SelectionBackground(model, vsDraw, eolInSelection).Opaque())); @@ -2584,10 +2708,10 @@ static ColourRGBA InvertedLight(ColourRGBA orig) noexcept { return ColourRGBA(std::min(r, 0xffu), std::min(g, 0xffu), std::min(b, 0xffu)); } -Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure, +Sci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangle rc, Surface *surface, Surface *surfaceMeasure, const EditModel &model, const ViewStyle &vs) { // Can't use measurements cached for screen - posCache.Clear(); + posCache->Clear(); ViewStyle vsPrint(vs); vsPrint.technology = Technology::Default; @@ -2608,6 +2732,7 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface // If this ever gets changed, cached pixmap would need to be recreated if technology != Technology::Default vsPrint.viewIndentationGuides = IndentView::None; // Don't show the selection when printing + vsPrint.selection.visible = false; vsPrint.elementColours.clear(); vsPrint.elementBaseColours.clear(); vsPrint.caretLine.alwaysShow = false; @@ -2649,15 +2774,15 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface vsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars); // Recalculate fixedColumnWidth } - const Sci::Line linePrintStart = model.pdoc->SciLineFromPosition(pfr->chrg.cpMin); - Sci::Line linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1; + const Sci::Line linePrintStart = model.pdoc->SciLineFromPosition(chrg.cpMin); + Sci::Line linePrintLast = linePrintStart + (rc.bottom - rc.top) / vsPrint.lineHeight - 1; if (linePrintLast < linePrintStart) linePrintLast = linePrintStart; - const Sci::Line linePrintMax = model.pdoc->SciLineFromPosition(pfr->chrg.cpMax); + const Sci::Line linePrintMax = model.pdoc->SciLineFromPosition(chrg.cpMax); if (linePrintLast > linePrintMax) linePrintLast = linePrintMax; //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n", - // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight, + // linePrintStart, linePrintLast, linePrintMax, rc.top, rc.bottom, vsPrint.lineHeight, // surfaceMeasure->Height(vsPrint.styles[StyleLineNumber].font)); Sci::Position endPosPrint = model.pdoc->Length(); if (linePrintLast < model.pdoc->LinesTotal()) @@ -2666,18 +2791,18 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface // Ensure we are styled to where we are formatting. model.pdoc->EnsureStyledTo(endPosPrint); - const int xStart = vsPrint.fixedColumnWidth + pfr->rc.left; - int ypos = pfr->rc.top; + const int xStart = vsPrint.fixedColumnWidth + rc.left; + int ypos = rc.top; Sci::Line lineDoc = linePrintStart; - Sci::Position nPrintPos = pfr->chrg.cpMin; + Sci::Position nPrintPos = chrg.cpMin; int visibleLine = 0; - int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth; + int widthPrint = rc.right - rc.left - vsPrint.fixedColumnWidth; if (printParameters.wrapState == Wrap::None) widthPrint = LineLayout::wrapWidthInfinite; - while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) { + while (lineDoc <= linePrintLast && ypos < rc.bottom) { // When printing, the hdc and hdcTarget may be the same, so // changing the state of surfaceMeasure may change the underlying @@ -2693,9 +2818,9 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface ll.containsCaret = false; PRectangle rcLine = PRectangle::FromInts( - pfr->rc.left, + rc.left, ypos, - pfr->rc.right - 1, + rc.right - 1, ypos + vsPrint.lineHeight); // When document line is wrapped over multiple display lines, find where @@ -2716,7 +2841,7 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface } if (draw && lineNumberWidth && - (ypos + vsPrint.lineHeight <= pfr->rc.bottom) && + (ypos + vsPrint.lineHeight <= rc.bottom) && (visibleLine >= 0)) { const std::string number = std::to_string(lineDoc + 1) + lineNumberPrintSpace; PRectangle rcNumber = rcLine; @@ -2735,7 +2860,7 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface surface->FlushCachedState(); for (int iwl = 0; iwl < ll.lines; iwl++) { - if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) { + if (ypos + vsPrint.lineHeight <= rc.bottom) { if (visibleLine >= 0) { if (draw) { rcLine.top = static_cast(ypos); @@ -2756,7 +2881,7 @@ Sci::Position EditView::FormatRange(bool draw, const RangeToFormat *pfr, Surface } // Clear cache so measurements are not used for screen - posCache.Clear(); + posCache->Clear(); return nPrintPos; } diff --git a/scintilla/src/EditView.h b/scintilla/src/EditView.h index bac59ed310..832da9f150 100644 --- a/scintilla/src/EditView.h +++ b/scintilla/src/EditView.h @@ -56,7 +56,6 @@ class EditView { std::unique_ptr ldTabstops; int tabWidthMinimumPixels; - bool hideSelection; bool drawOverstrikeCaret; // used by the curses platform /** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to @@ -81,7 +80,10 @@ class EditView { std::unique_ptr pixmapIndentGuideHighlight; LineLayoutCache llc; - PositionCache posCache; + std::unique_ptr posCache; + + unsigned int maxLayoutThreads; + static constexpr int bytesPerLayoutThread = 1000; int tabArrowHeight; // draw arrow heads this many pixels above/below line midpoint /** Some platforms, notably PLAT_CURSES, do not support Scintilla's native @@ -103,6 +105,9 @@ class EditView { bool SetPhasesDraw(int phases) noexcept; bool LinesOverlap() const noexcept; + void SetLayoutThreads(unsigned int threads) noexcept; + unsigned int GetLayoutThreads() const noexcept; + void ClearAllTabstops() noexcept; XYPOSITION NextTabstopPos(Sci::Line line, XYPOSITION x, XYPOSITION tabWidth) const noexcept; bool ClearTabstops(Sci::Line line) noexcept; @@ -115,7 +120,7 @@ class EditView { std::shared_ptr RetrieveLineLayout(Sci::Line lineNumber, const EditModel &model); void LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle, - LineLayout *ll, int width = LineLayout::wrapWidthInfinite); + LineLayout *ll, int width); static void UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, LineLayout *ll); @@ -154,7 +159,7 @@ class EditView { const ViewStyle &vsDraw); void FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line, PRectangle rcArea, int subLine) const; - Sci::Position FormatRange(bool draw, const Scintilla::RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure, + Sci::Position FormatRange(bool draw, CharacterRangeFull chrg, Rectangle rc, Surface *surface, Surface *surfaceMeasure, const EditModel &model, const ViewStyle &vs); }; diff --git a/scintilla/src/Editor.cxx b/scintilla/src/Editor.cxx index a47c9ce0de..32a63f14a2 100644 --- a/scintilla/src/Editor.cxx +++ b/scintilla/src/Editor.cxx @@ -204,7 +204,6 @@ Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) { Editor::~Editor() { pdoc->RemoveWatcher(this, nullptr); - DropGraphics(); } void Editor::Finalise() { @@ -264,12 +263,12 @@ void Editor::DropGraphics() noexcept { view.DropGraphics(); } -void Editor::InvalidateStyleData() { +void Editor::InvalidateStyleData() noexcept { stylesValid = false; vs.technology = technology; DropGraphics(); view.llc.Invalidate(LineLayout::ValidLevel::invalid); - view.posCache.Clear(); + view.posCache->Clear(); } void Editor::InvalidateStyleRedraw() { @@ -290,13 +289,17 @@ void Editor::RefreshStyleData() { } } +bool Editor::HasMarginWindow() const noexcept { + return wMargin.Created(); +} + Point Editor::GetVisibleOriginInMain() const { return Point(0, 0); } PointDocument Editor::DocumentPointFromView(Point ptView) const { PointDocument ptDocument(ptView); - if (wMargin.GetID()) { + if (HasMarginWindow()) { const Point ptOrigin = GetVisibleOriginInMain(); ptDocument.x += ptOrigin.x; ptDocument.y += ptOrigin.y; @@ -307,8 +310,8 @@ PointDocument Editor::DocumentPointFromView(Point ptView) const { return ptDocument; } -Sci::Line Editor::TopLineOfMain() const { - if (wMargin.GetID()) +Sci::Line Editor::TopLineOfMain() const noexcept { + if (HasMarginWindow()) return 0; else return topLine; @@ -433,7 +436,7 @@ Sci::Position Editor::PositionFromLineX(Sci::Line lineDoc, int x) { return SPositionFromLineX(lineDoc, x).Position(); } -Sci::Line Editor::LineFromLocation(Point pt) const { +Sci::Line Editor::LineFromLocation(Point pt) const noexcept { return pcs->DocFromDisplay(static_cast(pt.y) / vs.lineHeight + topLine); } @@ -486,7 +489,7 @@ void Editor::Redraw() { //Platform::DebugPrintf("Redraw all\n"); const PRectangle rcClient = GetClientRectangle(); wMain.InvalidateRectangle(rcClient); - if (wMargin.GetID()) { + if (HasMarginWindow()) { wMargin.InvalidateAll(); } else if (paintState == PaintState::notPainting) { redrawPendingText = true; @@ -495,12 +498,12 @@ void Editor::Redraw() { void Editor::RedrawSelMargin(Sci::Line line, bool allAfter) { const bool markersInText = vs.maskInLine || vs.maskDrawInText; - if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry + if (!HasMarginWindow() || markersInText) { // May affect text area so may need to abandon and retry if (AbandonPaint()) { return; } } - if (wMargin.GetID() && markersInText) { + if (HasMarginWindow() && markersInText) { Redraw(); return; } @@ -533,7 +536,7 @@ void Editor::RedrawSelMargin(Sci::Line line, bool allAfter) { if (rcMarkers.Empty()) return; } - if (wMargin.GetID()) { + if (HasMarginWindow()) { const Point ptOrigin = GetVisibleOriginInMain(); rcMarkers.Move(-ptOrigin.x, -ptOrigin.y); wMargin.InvalidateRectangle(rcMarkers); @@ -571,7 +574,7 @@ void Editor::InvalidateRange(Sci::Position start, Sci::Position end) { RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0)); } -Sci::Position Editor::CurrentPosition() const { +Sci::Position Editor::CurrentPosition() const noexcept { return sel.MainCaret(); } @@ -579,11 +582,11 @@ bool Editor::SelectionEmpty() const noexcept { return sel.Empty(); } -SelectionPosition Editor::SelectionStart() { +SelectionPosition Editor::SelectionStart() noexcept { return sel.RangeMain().Start(); } -SelectionPosition Editor::SelectionEnd() { +SelectionPosition Editor::SelectionEnd() noexcept { return sel.RangeMain().End(); } @@ -810,7 +813,7 @@ bool Editor::RangeContainsProtected(Sci::Position start, Sci::Position end) cons return false; } -bool Editor::SelectionContainsProtected() const { +bool Editor::SelectionContainsProtected() const noexcept { for (size_t r=0; rlines; } - return pcs->SetHeight(lineToWrap, linesWrapped + - ((vs.annotationVisible != AnnotationVisible::Hidden) ? pdoc->AnnotationLines(lineToWrap) : 0)); + if (vs.annotationVisible != AnnotationVisible::Hidden) { + linesWrapped += pdoc->AnnotationLines(lineToWrap); + } + return pcs->SetHeight(lineToWrap, linesWrapped); } // Perform wrapping for a subset of the lines needing wrapping. @@ -1516,8 +1521,11 @@ bool Editor::WrapLines(WrapScope ws) { if (wrapWidth != LineLayout::wrapWidthInfinite) { wrapWidth = LineLayout::wrapWidthInfinite; for (Sci::Line lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) { - pcs->SetHeight(lineDoc, 1 + - ((vs.annotationVisible != AnnotationVisible::Hidden) ? pdoc->AnnotationLines(lineDoc) : 0)); + int linesWrapped = 1; + if (vs.annotationVisible != AnnotationVisible::Hidden) { + linesWrapped += pdoc->AnnotationLines(lineDoc); + } + pcs->SetHeight(lineDoc, linesWrapped); } wrapOccurred = true; } @@ -1702,6 +1710,7 @@ void Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) { } else { surface = surfaceWindow; } + surface->SetMode(CurrentSurfaceMode()); // Clip vertically to paint area to avoid drawing line numbers if (rcMargin.bottom > rc.bottom) @@ -1829,18 +1838,30 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { // This is mostly copied from the Paint method but with some things omitted // such as the margin markers, line numbers, selection and caret // Should be merged back into a combined Draw method. -Sci::Position Editor::FormatRange(bool draw, const RangeToFormat *pfr) { - if (!pfr) - return 0; - - AutoSurface surface(pfr->hdc, this, Technology::Default); - if (!surface) - return 0; - AutoSurface surfaceMeasure(pfr->hdcTarget, this, Technology::Default); - if (!surfaceMeasure) { +Sci::Position Editor::FormatRange(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam) { + if (!lParam) return 0; + const bool draw = wParam != 0; + void *ptr = PtrFromSPtr(lParam); + if (iMessage == Message::FormatRange) { + RangeToFormat *pfr = static_cast(ptr); + CharacterRangeFull chrg{ pfr->chrg.cpMin,pfr->chrg.cpMax }; + AutoSurface surface(pfr->hdc, this, Technology::Default); + AutoSurface surfaceMeasure(pfr->hdcTarget, this, Technology::Default); + if (!surface || !surfaceMeasure) { + return 0; + } + return view.FormatRange(draw, chrg, pfr->rc, surface, surfaceMeasure, *this, vs); + } else { + // FormatRangeFull + RangeToFormatFull *pfr = static_cast(ptr); + AutoSurface surface(pfr->hdc, this, Technology::Default); + AutoSurface surfaceMeasure(pfr->hdcTarget, this, Technology::Default); + if (!surface || !surfaceMeasure) { + return 0; + } + return view.FormatRange(draw, pfr->chrg, pfr->rc, surface, surfaceMeasure, *this, vs); } - return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs); } long Editor::TextWidth(uptr_t style, const char *text) { @@ -1856,7 +1877,7 @@ long Editor::TextWidth(uptr_t style, const char *text) { // Empty method is overridden on GTK+ to show / hide scrollbars void Editor::ReconfigureScrollBars() {} -void Editor::SetScrollBars() { +void Editor::ChangeScrollBars() { RefreshStyleData(); const Sci::Line nMax = MaxScrollPos(); @@ -1880,6 +1901,11 @@ void Editor::SetScrollBars() { //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage); } +void Editor::SetScrollBars() { + // Overridden on GTK to defer to idle + ChangeScrollBars(); +} + void Editor::ChangeSize() { DropGraphics(); SetScrollBars(); @@ -1915,9 +1941,7 @@ SelectionPosition Editor::RealizeVirtualSpace(const SelectionPosition &position) } void Editor::AddChar(char ch) { - char s[2]; - s[0] = ch; - s[1] = '\0'; + const char s[1] {ch}; InsertCharacter(std::string_view(s, 1), CharacterSource::DirectInput); } @@ -1934,6 +1958,7 @@ void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) { return; } FilterSelections(); + bool wrapOccurred = false; { UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike); @@ -1981,17 +2006,17 @@ void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) { AutoSurface surface(this); if (surface) { if (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) { - SetScrollBars(); - SetVerticalScrollPos(); - Redraw(); + wrapOccurred = true; } } } } } } - if (Wrapping()) { + if (wrapOccurred) { SetScrollBars(); + SetVerticalScrollPos(); + Redraw(); } ThinRectangularRange(); // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information @@ -2333,15 +2358,6 @@ void Editor::DelCharBack(bool allowLineStartDeletion) { ShowCaretAtCurrentPosition(); } -KeyMod Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) noexcept { - return - (shift ? KeyMod::Shift : KeyMod::Norm) | - (ctrl ? KeyMod::Ctrl : KeyMod::Norm) | - (alt ? KeyMod::Alt : KeyMod::Norm) | - (meta ? KeyMod::Meta : KeyMod::Norm) | - (super ? KeyMod::Super : KeyMod::Norm); -} - void Editor::NotifyFocus(bool focus) { NotificationData scn = {}; scn.nmhdr.code = focus ? Notification::FocusIn : Notification::FocusOut; @@ -2363,9 +2379,6 @@ void Editor::NotifyStyleNeeded(Document *, void *, Sci::Position endStyleNeeded) NotifyStyleToNeeded(endStyleNeeded); } -void Editor::NotifyLexerChanged(Document *, void *) { -} - void Editor::NotifyErrorOccurred(Document *, void *, Status status) { errorStatus = status; } @@ -2382,6 +2395,9 @@ void Editor::NotifySavePoint(bool isSavePoint) { NotificationData scn = {}; if (isSavePoint) { scn.nmhdr.code = Notification::SavePointReached; + if (changeHistoryOption != ChangeHistoryOption::Disabled) { + Redraw(); + } } else { scn.nmhdr.code = Notification::SavePointLeft; } @@ -2710,6 +2726,9 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) { QueueIdleWork(WorkItems::style, mh.position + mh.length); } InvalidateRange(mh.position, mh.position + mh.length); + if (FlagSet(changeHistoryOption, ChangeHistoryOption::Markers)) { + RedrawSelMargin(pdoc->SciLineFromPosition(mh.position)); + } } } } @@ -2944,8 +2963,8 @@ void Editor::PageMove(int direction, Selection::SelTypes selt, bool stuttered) { if (topLineNew != topLine) { SetTopLine(topLineNew); MovePositionTo(newPos, selt); - Redraw(); SetVerticalScrollPos(); + Redraw(); } else { MovePositionTo(newPos, selt); } @@ -4073,17 +4092,9 @@ void Editor::Indent(bool forwards) { ContainerNeedsUpdate(Update::Selection); } -class CaseFolderASCII : public CaseFolderTable { -public: - CaseFolderASCII() noexcept { - StandardASCII(); - } -}; - - std::unique_ptr Editor::CaseFolderForEncoding() { // Simple default that only maps ASCII upper case to lower case. - return std::make_unique(); + return std::make_unique(); } /** @@ -4117,6 +4128,37 @@ Sci::Position Editor::FindText( } } +/** + * Search of a text in the document, in the given range. + * @return The position of the found text, -1 if not found. + */ +Sci::Position Editor::FindTextFull( + uptr_t wParam, ///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord, + ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix. + sptr_t lParam) { ///< @c Sci_TextToFindFull structure: The text to search for in the given range. + + TextToFindFull *ft = static_cast(PtrFromSPtr(lParam)); + Sci::Position lengthFound = strlen(ft->lpstrText); + if (!pdoc->HasCaseFolder()) + pdoc->SetCaseFolder(CaseFolderForEncoding()); + try { + const Sci::Position pos = pdoc->FindText( + static_cast(ft->chrg.cpMin), + static_cast(ft->chrg.cpMax), + ft->lpstrText, + static_cast(wParam), + &lengthFound); + if (pos != -1) { + ft->chrgText.cpMin = static_cast(pos); + ft->chrgText.cpMax = static_cast(pos + lengthFound); + } + return pos; + } catch (RegexError &) { + errorStatus = Status::RegEx; + return -1; + } +} + /** * Relocatable search support : Searches relative to current selection * point and sets the selection to the found text range with @@ -4128,7 +4170,7 @@ Sci::Position Editor::FindText( * while still setting the selection to found text so the find/select * operation is self-contained. */ -void Editor::SearchAnchor() { +void Editor::SearchAnchor() noexcept { searchAnchor = SelectionStart().Position(); } @@ -4734,7 +4776,7 @@ void Editor::RightButtonDownWithModifiers(Point pt, unsigned int, KeyMod modifie return; } -bool Editor::PositionIsHotspot(Sci::Position position) const { +bool Editor::PositionIsHotspot(Sci::Position position) const noexcept { return vs.styles[pdoc->StyleIndexAt(position)].hotspot; } @@ -5221,7 +5263,7 @@ bool Editor::PaintContains(PRectangle rc) { } bool Editor::PaintContainsMargin() { - if (wMargin.GetID()) { + if (HasMarginWindow()) { // With separate margin view, paint of text view // never contains margin. return false; @@ -5290,6 +5332,8 @@ void Editor::SetAnnotationHeights(Sci::Line start, Sci::Line end) { changedHeight = true; } if (changedHeight) { + SetScrollBars(); + SetVerticalScrollPos(); Redraw(); } } @@ -5366,18 +5410,23 @@ void Editor::SetEOLAnnotationVisible(EOLAnnotationVisible visible) { Sci::Line Editor::ExpandLine(Sci::Line line) { const Sci::Line lineMaxSubord = pdoc->GetLastChild(line); line++; + Sci::Line lineStart = line; while (line <= lineMaxSubord) { - pcs->SetVisible(line, line, true); const FoldLevel level = pdoc->GetFoldLevel(line); if (LevelIsHeader(level)) { + pcs->SetVisible(lineStart, line, true); if (pcs->GetExpanded(line)) { line = ExpandLine(line); } else { line = pdoc->GetLastChild(line); } + lineStart = line + 1; } line++; } + if (lineStart <= lineMaxSubord) { + pcs->SetVisible(lineStart, lineMaxSubord, true); + } return lineMaxSubord; } @@ -5524,35 +5573,41 @@ void Editor::EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy) { } void Editor::FoldAll(FoldAction action) { - pdoc->EnsureStyledTo(pdoc->Length()); const Sci::Line maxLine = pdoc->LinesTotal(); + const bool contractAll = FlagSet(action, FoldAction::ContractEveryLevel); + action = static_cast(static_cast(action) & ~static_cast(FoldAction::ContractEveryLevel)); bool expanding = action == FoldAction::Expand; + if (!expanding) { + pdoc->EnsureStyledTo(pdoc->Length()); + } + Sci::Line line = 0; if (action == FoldAction::Toggle) { // Discover current state - for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) { - if (LevelIsHeader(pdoc->GetFoldLevel(lineSeek))) { - expanding = !pcs->GetExpanded(lineSeek); + for (; line < maxLine; line++) { + if (LevelIsHeader(pdoc->GetFoldLevel(line))) { + expanding = !pcs->GetExpanded(line); break; } } } if (expanding) { pcs->SetVisible(0, maxLine-1, true); - for (int line = 0; line < maxLine; line++) { - const FoldLevel levelLine = pdoc->GetFoldLevel(line); - if (LevelIsHeader(levelLine)) { - SetFoldExpanded(line, true); - } - } + pcs->ExpandAll(); } else { - for (Sci::Line line = 0; line < maxLine; line++) { + for (; line < maxLine; line++) { const FoldLevel level = pdoc->GetFoldLevel(line); - if (LevelIsHeader(level) && - (FoldLevel::Base == LevelNumberPart(level))) { - SetFoldExpanded(line, false); - const Sci::Line lineMaxSubord = pdoc->GetLastChild(line); - if (lineMaxSubord > line) { - pcs->SetVisible(line + 1, lineMaxSubord, false); + if (LevelIsHeader(level)) { + if (FoldLevel::Base == LevelNumberPart(level)) { + SetFoldExpanded(line, false); + const Sci::Line lineMaxSubord = pdoc->GetLastChild(line); + if (lineMaxSubord > line) { + pcs->SetVisible(line + 1, lineMaxSubord, false); + if (!contractAll) { + line = lineMaxSubord; + } + } + } else if (contractAll) { + SetFoldExpanded(line, false); } } } @@ -5679,6 +5734,26 @@ int Editor::CodePage() const noexcept { return 0; } +std::unique_ptr Editor::CreateMeasurementSurface() const { + if (!wMain.GetID()) { + return {}; + } + std::unique_ptr surf = Surface::Allocate(technology); + surf->Init(wMain.GetID()); + surf->SetMode(CurrentSurfaceMode()); + return surf; +} + +std::unique_ptr Editor::CreateDrawingSurface(SurfaceID sid, std::optional technologyOpt) const { + if (!wMain.GetID()) { + return {}; + } + std::unique_ptr surf = Surface::Allocate(technologyOpt ? *technologyOpt : technology); + surf->Init(sid, wMain.GetID()); + surf->SetMode(CurrentSurfaceMode()); + return surf; +} + Sci::Line Editor::WrapCount(Sci::Line line) { AutoSurface surface(this); std::shared_ptr ll = view.RetrieveLineLayout(line, *this); @@ -6218,6 +6293,9 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::FindText: return FindText(wParam, lParam); + case Message::FindTextFull: + return FindTextFull(wParam, lParam); + case Message::GetTextRange: { if (lParam == 0) return 0; @@ -6233,13 +6311,34 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { return len; // Not including NUL } + case Message::GetTextRangeFull: { + if (lParam == 0) + return 0; + TextRangeFull *tr = static_cast(PtrFromSPtr(lParam)); + Sci::Position cpMax = tr->chrg.cpMax; + if (cpMax == -1) + cpMax = pdoc->Length(); + PLATFORM_ASSERT(cpMax <= pdoc->Length()); + const Sci::Position len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions + PLATFORM_ASSERT(len >= 0); + pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len); + // Spec says copied text is terminated with a NUL + tr->lpstrText[len] = '\0'; + return len; // Not including NUL + } + case Message::HideSelection: - view.hideSelection = wParam != 0; + vs.selection.visible = wParam == 0; Redraw(); break; + case Message::GetSelectionHidden: + return !vs.selection.visible; + break; + case Message::FormatRange: - return FormatRange(wParam != 0, static_cast(PtrFromSPtr(lParam))); + case Message::FormatRangeFull: + return FormatRange(iMessage, wParam, lParam); case Message::GetMarginLeft: return vs.leftMarginWidth; @@ -6456,6 +6555,12 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { else return pdoc->StyleAt(PositionFromUPtr(wParam)); + case Message::GetStyleIndexAt: + if (PositionFromUPtr(wParam) >= pdoc->Length()) + return 0; + else + return pdoc->StyleIndexAt(PositionFromUPtr(wParam)); + case Message::Redo: Redo(); break; @@ -6799,11 +6904,18 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { return static_cast(view.llc.GetLevel()); case Message::SetPositionCache: - view.posCache.SetSize(wParam); + view.posCache->SetSize(wParam); break; case Message::GetPositionCache: - return view.posCache.GetSize(); + return view.posCache->GetSize(); + + case Message::SetLayoutThreads: + view.SetLayoutThreads(static_cast(wParam)); + break; + + case Message::GetLayoutThreads: + return view.GetLayoutThreads(); case Message::SetScrollWidth: PLATFORM_ASSERT(wParam > 0); @@ -7064,14 +7176,14 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { break; case Message::MarkerGet: - return pdoc->GetMark(LineFromUPtr(wParam)); + return GetMark(LineFromUPtr(wParam)); case Message::MarkerNext: return pdoc->MarkerNext(LineFromUPtr(wParam), static_cast(lParam)); case Message::MarkerPrevious: { for (Sci::Line iLine = LineFromUPtr(wParam); iLine >= 0; iLine--) { - if ((pdoc->GetMark(iLine) & lParam) != 0) + if ((GetMark(iLine) & lParam) != 0) return iLine; } } @@ -8242,6 +8354,14 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::GetGapPosition: return pdoc->GapPosition(); + case Message::SetChangeHistory: + changeHistoryOption = static_cast(wParam); + pdoc->ChangeHistorySet(wParam & 1); + break; + + case Message::GetChangeHistory: + return static_cast(changeHistoryOption); + case Message::SetExtraAscent: vs.extraAscent = static_cast(wParam); InvalidateStyleRedraw(); diff --git a/scintilla/src/Editor.h b/scintilla/src/Editor.h index f3e23ef5b5..297590f47a 100644 --- a/scintilla/src/Editor.h +++ b/scintilla/src/Editor.h @@ -296,17 +296,18 @@ class Editor : public EditModel, public DocWatcher { virtual void Initialise() = 0; virtual void Finalise(); - void InvalidateStyleData(); + void InvalidateStyleData() noexcept; void InvalidateStyleRedraw(); void RefreshStyleData(); void SetRepresentations(); void DropGraphics() noexcept; + bool HasMarginWindow() const noexcept; // The top left visible point in main window coordinates. Will be 0,0 except for // scroll views where it will be equivalent to the current scroll position. Point GetVisibleOriginInMain() const override; PointDocument DocumentPointFromView(Point ptView) const; // Convert a point from view space to document - Sci::Line TopLineOfMain() const override; // Return the line at Main's y coordinate 0 + Sci::Line TopLineOfMain() const noexcept final; // Return the line at Main's y coordinate 0 virtual PRectangle GetClientRectangle() const; virtual PRectangle GetClientDrawingRectangle(); PRectangle GetTextRectangle() const; @@ -322,7 +323,7 @@ class Editor : public EditModel, public DocWatcher { Sci::Position PositionFromLocation(Point pt, bool canReturnInvalid = false, bool charPosition = false); SelectionPosition SPositionFromLineX(Sci::Line lineDoc, int x); Sci::Position PositionFromLineX(Sci::Line lineDoc, int x); - Sci::Line LineFromLocation(Point pt) const; + Sci::Line LineFromLocation(Point pt) const noexcept; void SetTopLine(Sci::Line topLineNew); virtual bool AbandonPaint(); @@ -336,10 +337,10 @@ class Editor : public EditModel, public DocWatcher { bool UserVirtualSpace() const noexcept { return (FlagSet(virtualSpaceOptions, Scintilla::VirtualSpace::UserAccessible)); } - Sci::Position CurrentPosition() const; + Sci::Position CurrentPosition() const noexcept; bool SelectionEmpty() const noexcept; - SelectionPosition SelectionStart(); - SelectionPosition SelectionEnd(); + SelectionPosition SelectionStart() noexcept; + SelectionPosition SelectionEnd() noexcept; void SetRectangularRange(); void ThinRectangularRange(); void InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection=false); @@ -353,7 +354,7 @@ class Editor : public EditModel, public DocWatcher { enum class AddNumber { one, each }; void MultipleSelectAdd(AddNumber addNumber); bool RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept; - bool SelectionContainsProtected() const; + bool SelectionContainsProtected() const noexcept; Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd=true) const; SelectionPosition MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd=true) const; void MovedCaret(SelectionPosition newPos, SelectionPosition previousPos, @@ -406,14 +407,15 @@ class Editor : public EditModel, public DocWatcher { void PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc); void RefreshPixMaps(Surface *surfaceWindow); void Paint(Surface *surfaceWindow, PRectangle rcArea); - Sci::Position FormatRange(bool draw, const Scintilla::RangeToFormat *pfr); + Sci::Position FormatRange(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); long TextWidth(Scintilla::uptr_t style, const char *text); virtual void SetVerticalScrollPos() = 0; virtual void SetHorizontalScrollPos() = 0; virtual bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) = 0; virtual void ReconfigureScrollBars(); - void SetScrollBars(); + void ChangeScrollBars(); + virtual void SetScrollBars(); void ChangeSize(); void FilterSelections(); @@ -441,7 +443,6 @@ class Editor : public EditModel, public DocWatcher { void DelCharBack(bool allowLineStartDeletion); virtual void ClaimSelection() = 0; - static Scintilla::KeyMod ModifierFlags(bool shift, bool ctrl, bool alt, bool meta=false, bool super=false) noexcept; virtual void NotifyChange() = 0; virtual void NotifyFocus(bool focus); virtual void SetCtrlID(int identifier); @@ -470,7 +471,6 @@ class Editor : public EditModel, public DocWatcher { void NotifyModified(Document *document, DocModification mh, void *userData) override; void NotifyDeleted(Document *document, void *userData) noexcept override; void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endStyleNeeded) override; - void NotifyLexerChanged(Document *doc, void *userData) override; void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) override; void NotifyMacroRecord(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); @@ -502,7 +502,8 @@ class Editor : public EditModel, public DocWatcher { virtual std::unique_ptr CaseFolderForEncoding(); Sci::Position FindText(Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); - void SearchAnchor(); + Sci::Position FindTextFull(Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); + void SearchAnchor() noexcept; Sci::Position SearchText(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); Sci::Position SearchInTarget(const char *text, Sci::Position length); void GoToLine(Sci::Line lineNo); @@ -582,7 +583,7 @@ class Editor : public EditModel, public DocWatcher { Sci::Position GetTag(char *tagValue, int tagNumber); Sci::Position ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length=-1); - bool PositionIsHotspot(Sci::Position position) const; + bool PositionIsHotspot(Sci::Position position) const noexcept; bool PointIsHotspot(Point pt); void SetHotSpotRange(const Point *pt); void SetHoverIndicatorPosition(Sci::Position position); @@ -592,6 +593,8 @@ class Editor : public EditModel, public DocWatcher { virtual bool ValidCodePage(int /* codePage */) const { return true; } virtual std::string UTF8FromEncoded(std::string_view encoded) const = 0; virtual std::string EncodedFromUTF8(std::string_view utf8) const = 0; + virtual std::unique_ptr CreateMeasurementSurface() const; + virtual std::unique_ptr CreateDrawingSurface(SurfaceID sid, std::optional technologyOpt = {}) const; Sci::Line WrapCount(Sci::Line line); void AddStyledText(const char *buffer, Sci::Position appendLength); @@ -684,19 +687,11 @@ class AutoSurface { private: std::unique_ptr surf; public: - AutoSurface(const Editor *ed) { - if (ed->wMain.GetID()) { - surf = Surface::Allocate(ed->technology); - surf->Init(ed->wMain.GetID()); - surf->SetMode(SurfaceMode(ed->CodePage(), ed->BidirectionalR2L())); - } + AutoSurface(const Editor *ed) : + surf(ed->CreateMeasurementSurface()) { } - AutoSurface(SurfaceID sid, Editor *ed, std::optional technology = {}) { - if (ed->wMain.GetID()) { - surf = Surface::Allocate(technology ? *technology : ed->technology); - surf->Init(sid, ed->wMain.GetID()); - surf->SetMode(SurfaceMode(ed->CodePage(), ed->BidirectionalR2L())); - } + AutoSurface(SurfaceID sid, Editor *ed, std::optional technology = {}) : + surf(ed->CreateDrawingSurface(sid, technology)) { } // Deleted so AutoSurface objects can not be copied. AutoSurface(const AutoSurface &) = delete; diff --git a/scintilla/src/FontQuality.h b/scintilla/src/FontQuality.h deleted file mode 100644 index b587fc6d40..0000000000 --- a/scintilla/src/FontQuality.h +++ /dev/null @@ -1,27 +0,0 @@ -// Scintilla source code edit control -/** @file FontQuality.h - ** Definitions to control font anti-aliasing. - ** Redefine constants from Scintilla.h to avoid including Scintilla.h in PlatWin.cxx. - **/ -// Copyright 1998-2009 by Neil Hodgson -// The License.txt file describes the conditions under which this software may be distributed. - -#ifndef FONTQUALITY_H -#define FONTQUALITY_H - -namespace Scintilla { - -// These definitions match Scintilla.h -//#define SC_EFF_QUALITY_MASK 0xF -//#define SC_EFF_QUALITY_DEFAULT 0 -//#define SC_EFF_QUALITY_NON_ANTIALIASED 1 -//#define SC_EFF_QUALITY_ANTIALIASED 2 -//#define SC_EFF_QUALITY_LCD_OPTIMIZED 3 - -// These definitions must match SC_TECHNOLOGY_* in Scintilla.h -//#define SCWIN_TECH_GDI 0 -//#define SCWIN_TECH_DIRECTWRITE 1 - -} - -#endif diff --git a/scintilla/src/Geometry.cxx b/scintilla/src/Geometry.cxx index 97e99d431b..c6247f7559 100644 --- a/scintilla/src/Geometry.cxx +++ b/scintilla/src/Geometry.cxx @@ -75,10 +75,14 @@ XYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept { return std::floor(xy * pixelDivisions) / pixelDivisions; } +XYPOSITION PixelAlignCeil(XYPOSITION xy, int pixelDivisions) noexcept { + return std::ceil(xy * pixelDivisions) / pixelDivisions; +} + Point PixelAlign(const Point &pt, int pixelDivisions) noexcept { return Point( - std::round(pt.x * pixelDivisions) / pixelDivisions, - std::round(pt.y * pixelDivisions) / pixelDivisions); + PixelAlign(pt.x, pixelDivisions), + PixelAlign(pt.y, pixelDivisions)); } PRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept { @@ -88,19 +92,19 @@ PRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept { // On retina displays, the positions should be moved to the nearest device // pixel which is the nearest half logical pixel. return PRectangle( - std::round(rc.left * pixelDivisions) / pixelDivisions, + PixelAlign(rc.left, pixelDivisions), PixelAlignFloor(rc.top, pixelDivisions), - std::round(rc.right * pixelDivisions) / pixelDivisions, + PixelAlign(rc.right, pixelDivisions), PixelAlignFloor(rc.bottom, pixelDivisions)); } PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept { // Move left and right side to extremes (floor(left) ceil(right)) to avoid blurry visuals. return PRectangle( - std::floor(rc.left * pixelDivisions) / pixelDivisions, - std::floor(rc.top * pixelDivisions) / pixelDivisions, - std::ceil(rc.right * pixelDivisions) / pixelDivisions, - std::floor(rc.bottom * pixelDivisions) / pixelDivisions); + PixelAlignFloor(rc.left, pixelDivisions), + PixelAlignFloor(rc.top, pixelDivisions), + PixelAlignCeil(rc.right, pixelDivisions), + PixelAlignFloor(rc.bottom, pixelDivisions)); } ColourRGBA ColourRGBA::MixedWith(ColourRGBA other) const noexcept { diff --git a/scintilla/src/Geometry.h b/scintilla/src/Geometry.h index 5a9b3e2918..4fd90afad2 100644 --- a/scintilla/src/Geometry.h +++ b/scintilla/src/Geometry.h @@ -149,6 +149,7 @@ Interval HorizontalBounds(PRectangle rc) noexcept; XYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept; XYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept; +XYPOSITION PixelAlignCeil(XYPOSITION xy, int pixelDivisions) noexcept; Point PixelAlign(const Point &pt, int pixelDivisions) noexcept; diff --git a/scintilla/src/Indicator.cxx b/scintilla/src/Indicator.cxx index bd02a01d1d..cb7d5deba5 100644 --- a/scintilla/src/Indicator.cxx +++ b/scintilla/src/Indicator.cxx @@ -252,7 +252,7 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r case IndicatorStyle::CompositionThick: { const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom); - surface->FillRectangle(rcComposition, sacDraw.fore); + surface->FillRectangle(rcComposition, ColourRGBA(sacDraw.fore, outlineAlpha)); } break; @@ -269,7 +269,7 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r const XYPOSITION x = (sacDraw.style == IndicatorStyle::Point) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2); // 0.5f is to hit midpoint of pixels: const XYPOSITION ix = std::round(x) + 0.5f; - const XYPOSITION iy = std::floor(rc.top + 1.0f) + 0.5f; + const XYPOSITION iy = std::floor(rc.top) + 0.5f; const Point pts[] = { Point(ix - pixelHeight, iy + pixelHeight), // Left Point(ix + pixelHeight, iy + pixelHeight), // Right @@ -279,6 +279,22 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r } break; + case IndicatorStyle::PointTop: + if (rcCharacter.Width() >= 0.1) { + const XYPOSITION pixelHeight = std::floor(rc.Height() - 1.0f); // 1 pixel onto previous line if multiphase + const XYPOSITION x = rcCharacter.left; + // 0.5f is to hit midpoint of pixels: + const XYPOSITION ix = std::round(x) + 0.5f; + const XYPOSITION iy = std::floor(rcLine.top) + 0.5f; + const Point pts[] = { + Point(ix - pixelHeight, iy), // Left + Point(ix + pixelHeight, iy), // Right + Point(ix, iy + pixelHeight) // Bottom + }; + surface->Polygon(pts, std::size(pts), FillStroke(sacDraw.fore)); + } + break; + default: // Either IndicatorStyle::Plain or unknown surface->FillRectangle(PRectangle(rcAligned.left, ymid, diff --git a/scintilla/src/KeyMap.cxx b/scintilla/src/KeyMap.cxx index 6e112479df..da4f242a4e 100644 --- a/scintilla/src/KeyMap.cxx +++ b/scintilla/src/KeyMap.cxx @@ -18,6 +18,8 @@ #include "ScintillaMessages.h" #include "Debugging.h" +#include "Geometry.h" +#include "Platform.h" #include "KeyMap.h" @@ -56,7 +58,7 @@ const std::map &KeyMap::GetKeyMap() const noexcept { #endif // Define a modifier that is exactly Ctrl key on all platforms -// Most uses of Ctrl map to Cmd on OS X but some can't so use SCI_[S]CTRL_META +// Most uses of Ctrl map to Cmd on macOS but some can't so use SCI_[S]CTRL_META #if OS_X_KEYS #define SCI_CTRL_META SCI_META #define SCI_SCTRL_META (SCI_META | SCI_SHIFT) diff --git a/scintilla/src/KeyMap.h b/scintilla/src/KeyMap.h index bcdee9c818..673f381f44 100644 --- a/scintilla/src/KeyMap.h +++ b/scintilla/src/KeyMap.h @@ -25,6 +25,8 @@ class KeyModifiers { public: Scintilla::Keys key; Scintilla::KeyMod modifiers; + KeyModifiers() noexcept : key{}, modifiers(KeyMod::Norm) { + }; KeyModifiers(Scintilla::Keys key_, Scintilla::KeyMod modifiers_) noexcept : key(key_), modifiers(modifiers_) { } bool operator<(const KeyModifiers &other) const noexcept { diff --git a/scintilla/src/LineMarker.cxx b/scintilla/src/LineMarker.cxx index df8c3d55aa..9794f523c0 100644 --- a/scintilla/src/LineMarker.cxx +++ b/scintilla/src/LineMarker.cxx @@ -514,6 +514,37 @@ void LineMarker::Draw(Surface *surface, const PRectangle &rcWhole, const Font *f } break; + case MarkerSymbol::Bar: { + PRectangle rcBar = rcWhole; + const XYPOSITION widthBar = std::floor(rcWhole.Width() / 3.0); + rcBar.left = centreX - std::floor(widthBar / 2.0); + rcBar.right = rcBar.left + widthBar; + surface->SetClip(rcWhole); + switch (part) { + case LineMarker::FoldPart::headWithTail: + surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth)); + break; + case LineMarker::FoldPart::head: + rcBar.bottom += 5; + surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth)); + break; + case LineMarker::FoldPart::tail: + rcBar.top -= 5; + surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth)); + break; + case LineMarker::FoldPart::body: + rcBar.top -= 5; + rcBar.bottom += 5; + surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth)); + break; + default: + break; + } + surface->PopClip(); + } + break; + + case MarkerSymbol::Bookmark: { const XYPOSITION halfHeight = std::floor(minDim / 3); Point pts[] = { diff --git a/scintilla/src/MarginView.cxx b/scintilla/src/MarginView.cxx index cc243f41fd..6e43c0f0cd 100644 --- a/scintilla/src/MarginView.cxx +++ b/scintilla/src/MarginView.cxx @@ -283,7 +283,11 @@ void MarginView::PaintOneMargin(Surface *surface, PRectangle rc, PRectangle rcOn const bool firstSubLine = visibleLine == firstVisibleLine; const bool lastSubLine = visibleLine == lastVisibleLine; - int marks = firstSubLine ? model.pdoc->GetMark(lineDoc) : 0; + int marks = model.GetMark(lineDoc); + if (!firstSubLine) { + // Mask off non-continuing marks + marks = marks & vs.maskDrawWrapped; + } bool headWithTail = false; @@ -413,6 +417,24 @@ void MarginView::PaintOneMargin(Surface *surface, PRectangle rc, PRectangle rcOn part = LineMarker::FoldPart::tail; } } + if (vs.markers[markBit].markType == MarkerSymbol::Bar) { + const int mask = 1 << markBit; + const bool markBefore = firstSubLine ? (model.GetMark(lineDoc-1) & mask) : true; + const bool markAfter = lastSubLine ? (model.GetMark(lineDoc+1) & mask) : true; + if (markBefore) { + if (markAfter) { + part = LineMarker::FoldPart::body; + } else { + part = LineMarker::FoldPart::tail; + } + } else { + if (markAfter) { + part = LineMarker::FoldPart::head; + } else { + part = LineMarker::FoldPart::headWithTail; + } + } + } vs.markers[markBit].Draw(surface, rcMarker, vs.styles[StyleLineNumber].font.get(), part, marginStyle.style); } marks >>= 1; diff --git a/scintilla/src/Partitioning.h b/scintilla/src/Partitioning.h index efa6bf95ef..17bc1998eb 100644 --- a/scintilla/src/Partitioning.h +++ b/scintilla/src/Partitioning.h @@ -10,44 +10,6 @@ namespace Scintilla::Internal { -/// A split vector of integers with a method for adding a value to all elements -/// in a range. -/// Used by the Partitioning class. - -template -class SplitVectorWithRangeAdd : public SplitVector { -public: - explicit SplitVectorWithRangeAdd(ptrdiff_t growSize_) { - this->SetGrowSize(growSize_); - this->ReAllocate(growSize_); - } - // Deleted so SplitVectorWithRangeAdd objects can not be copied. - SplitVectorWithRangeAdd(const SplitVectorWithRangeAdd &) = delete; - SplitVectorWithRangeAdd(SplitVectorWithRangeAdd &&) = delete; - void operator=(const SplitVectorWithRangeAdd &) = delete; - void operator=(SplitVectorWithRangeAdd &&) = delete; - ~SplitVectorWithRangeAdd() { - } - void RangeAddDelta(ptrdiff_t start, ptrdiff_t end, T delta) noexcept { - // end is 1 past end, so end-start is number of elements to change - ptrdiff_t i = 0; - const ptrdiff_t rangeLength = end - start; - ptrdiff_t range1Length = rangeLength; - const ptrdiff_t part1Left = this->part1Length - start; - if (range1Length > part1Left) - range1Length = part1Left; - while (i < range1Length) { - this->body[start++] += delta; - i++; - } - start += this->gapLength; - while (i < rangeLength) { - this->body[start++] += delta; - i++; - } - } -}; - /// Divide an interval into multiple partitions. /// Useful for breaking a document down into sections such as lines. /// A 0 length interval has a single 0 length partition, numbered 0 @@ -62,15 +24,41 @@ class Partitioning { // there may be a step somewhere in the list. T stepPartition; T stepLength; - std::unique_ptr> body; + SplitVector body; + + // Deleted so SplitVectorWithRangeAdd objects can not be copied. + void RangeAddDelta(T start, T end, T delta) noexcept { + // end is 1 past end, so end-start is number of elements to change + const ptrdiff_t position = start; + ptrdiff_t i = 0; + const ptrdiff_t rangeLength = end - position; + ptrdiff_t range1Length = rangeLength; + const ptrdiff_t part1Left = body.GapPosition() - position; + if (range1Length > part1Left) + range1Length = part1Left; + T *writer = &body[position]; + while (i < range1Length) { + *writer += delta; + writer++; + i++; + } + if (i < rangeLength) { + T *writer2 = &body[position + i]; + while (i < rangeLength) { + *writer2 += delta; + writer2++; + i++; + } + } + } // Move step forward void ApplyStep(T partitionUpTo) noexcept { if (stepLength != 0) { - body->RangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength); + RangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength); } stepPartition = partitionUpTo; - if (stepPartition >= body->Length()-1) { + if (stepPartition >= Partitions()) { stepPartition = Partitions(); stepLength = 0; } @@ -79,40 +67,24 @@ class Partitioning { // Move step backward void BackStep(T partitionDownTo) noexcept { if (stepLength != 0) { - body->RangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength); + RangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength); } stepPartition = partitionDownTo; } - void Allocate(ptrdiff_t growSize) { - body = std::make_unique>(growSize); - stepPartition = 0; - stepLength = 0; - body->Insert(0, 0); // This value stays 0 for ever - body->Insert(1, 0); // This is the end of the first partition and will be the start of the second - } - public: - explicit Partitioning(int growSize) : stepPartition(0), stepLength(0) { - Allocate(growSize); - } - - // Deleted so Partitioning objects can not be copied. - Partitioning(const Partitioning &) = delete; - Partitioning(Partitioning &&) = delete; - void operator=(const Partitioning &) = delete; - void operator=(Partitioning &&) = delete; - - ~Partitioning() { + explicit Partitioning(size_t growSize=8) : stepPartition(0), stepLength(0), body(growSize) { + body.Insert(0, 0); // This value stays 0 for ever + body.Insert(1, 0); // This is the end of the first partition and will be the start of the second } T Partitions() const noexcept { - return static_cast(body->Length())-1; + return static_cast(body.Length())-1; } void ReAllocate(ptrdiff_t newSize) { // + 1 accounts for initial element that is always 0. - body->ReAllocate(newSize + 1); + body.ReAllocate(newSize + 1); } T Length() const noexcept { @@ -123,7 +95,7 @@ class Partitioning { if (stepPartition < partition) { ApplyStep(partition); } - body->Insert(partition, pos); + body.Insert(partition, pos); stepPartition++; } @@ -131,7 +103,7 @@ class Partitioning { if (stepPartition < partition) { ApplyStep(partition); } - body->InsertFromArray(partition, positions, 0, length); + body.InsertFromArray(partition, positions, 0, length); stepPartition += static_cast(length); } @@ -140,7 +112,7 @@ class Partitioning { if (stepPartition < partition) { ApplyStep(partition); } - T *pInsertion = body->InsertEmpty(partition, length); + T *pInsertion = body.InsertEmpty(partition, length); for (size_t i = 0; i < length; i++) { pInsertion[i] = static_cast(positions[i]); } @@ -149,10 +121,10 @@ class Partitioning { void SetPartitionStartPosition(T partition, T pos) noexcept { ApplyStep(partition+1); - if ((partition < 0) || (partition > body->Length())) { + if ((partition < 0) || (partition >= body.Length())) { return; } - body->SetValueAt(partition, pos); + body.SetValueAt(partition, pos); } void InsertText(T partitionInsert, T delta) noexcept { @@ -162,7 +134,7 @@ class Partitioning { // Fill in up to the new insertion point ApplyStep(partitionInsert); stepLength += delta; - } else if (partitionInsert >= (stepPartition - body->Length() / 10)) { + } else if (partitionInsert >= (stepPartition - body.Length() / 10)) { // Close to step but before so move step back BackStep(partitionInsert); stepLength += delta; @@ -184,17 +156,17 @@ class Partitioning { } else { stepPartition--; } - body->Delete(partition); + body.Delete(partition); } T PositionFromPartition(T partition) const noexcept { PLATFORM_ASSERT(partition >= 0); - PLATFORM_ASSERT(partition < body->Length()); - const ptrdiff_t lengthBody = body->Length(); + PLATFORM_ASSERT(partition < body.Length()); + const ptrdiff_t lengthBody = body.Length(); if ((partition < 0) || (partition >= lengthBody)) { return 0; } - T pos = body->ValueAt(partition); + T pos = body.ValueAt(partition); if (partition > stepPartition) pos += stepLength; return pos; @@ -202,7 +174,7 @@ class Partitioning { /// Return value in range [0 .. Partitions() - 1] even for arguments outside interval T PartitionFromPosition(T pos) const noexcept { - if (body->Length() <= 1) + if (body.Length() <= 1) return 0; if (pos >= (PositionFromPartition(Partitions()))) return Partitions() - 1; @@ -210,7 +182,7 @@ class Partitioning { T upper = Partitions(); do { const T middle = (upper + lower + 1) / 2; // Round high - T posMiddle = body->ValueAt(middle); + T posMiddle = body.ValueAt(middle); if (middle > stepPartition) posMiddle += stepLength; if (pos < posMiddle) { @@ -223,7 +195,11 @@ class Partitioning { } void DeleteAll() { - Allocate(body->GetGrowSize()); + body.DeleteAll(); + stepPartition = 0; + stepLength = 0; + body.Insert(0, 0); // This value stays 0 for ever + body.Insert(1, 0); // This is the end of the first partition and will be the start of the second } void Check() const { diff --git a/scintilla/src/PerLine.cxx b/scintilla/src/PerLine.cxx index 762891ee30..78f7c7dc57 100644 --- a/scintilla/src/PerLine.cxx +++ b/scintilla/src/PerLine.cxx @@ -34,10 +34,6 @@ using namespace Scintilla::Internal; MarkerHandleSet::MarkerHandleSet() { } -MarkerHandleSet::~MarkerHandleSet() { - mhList.clear(); -} - bool MarkerHandleSet::Empty() const noexcept { return mhList.empty(); } @@ -93,9 +89,6 @@ void MarkerHandleSet::CombineWith(MarkerHandleSet *other) noexcept { mhList.splice_after(mhList.before_begin(), other->mhList); } -LineMarkers::~LineMarkers() { -} - void LineMarkers::Init() { markers.DeleteAll(); } @@ -219,9 +212,6 @@ void LineMarkers::DeleteMarkFromHandle(int markerHandle) { } } -LineLevels::~LineLevels() { -} - void LineLevels::Init() { levels.DeleteAll(); } @@ -262,15 +252,13 @@ void LineLevels::ClearLevels() { } int LineLevels::SetLevel(Sci::Line line, int level, Sci::Line lines) { - int prev = 0; + int prev = level; if ((line >= 0) && (line < lines)) { if (!levels.Length()) { ExpandLevels(lines + 1); } prev = levels[line]; - if (prev != level) { - levels[line] = level; - } + levels[line] = level; } return prev; } @@ -283,9 +271,6 @@ int LineLevels::GetLevel(Sci::Line line) const noexcept { } } -LineState::~LineState() { -} - void LineState::Init() { lineStates.DeleteAll(); } @@ -312,10 +297,13 @@ void LineState::RemoveLine(Sci::Line line) { } } -int LineState::SetLineState(Sci::Line line, int state) { - lineStates.EnsureLength(line + 1); - const int stateOld = lineStates[line]; - lineStates[line] = state; +int LineState::SetLineState(Sci::Line line, int state, Sci::Line lines) { + int stateOld = state; + if ((line >= 0) && (line < lines)) { + lineStates.EnsureLength(lines + 1); + stateOld = lineStates[line]; + lineStates[line] = state; + } return stateOld; } @@ -354,7 +342,8 @@ std::unique_ptrAllocateAnnotation(size_t length, int style) { } -LineAnnotation::~LineAnnotation() { +bool LineAnnotation::Empty() const noexcept { + return annotations.Length() == 0; } void LineAnnotation::Init() { @@ -477,9 +466,6 @@ int LineAnnotation::Lines(Sci::Line line) const noexcept { return 0; } -LineTabstops::~LineTabstops() { -} - void LineTabstops::Init() { tabstops.DeleteAll(); } diff --git a/scintilla/src/PerLine.h b/scintilla/src/PerLine.h index 8f88183f1b..5b57dcaa9e 100644 --- a/scintilla/src/PerLine.h +++ b/scintilla/src/PerLine.h @@ -28,12 +28,6 @@ class MarkerHandleSet { public: MarkerHandleSet(); - // Deleted so MarkerHandleSet objects can not be copied. - MarkerHandleSet(const MarkerHandleSet &) = delete; - MarkerHandleSet(MarkerHandleSet &&) = delete; - void operator=(const MarkerHandleSet &) = delete; - void operator=(MarkerHandleSet &&) = delete; - ~MarkerHandleSet(); bool Empty() const noexcept; int MarkValue() const noexcept; ///< Bit set of marker numbers. bool Contains(int handle) const noexcept; @@ -51,12 +45,6 @@ class LineMarkers : public PerLine { public: LineMarkers() : handleCurrent(0) { } - // Deleted so LineMarkers objects can not be copied. - LineMarkers(const LineMarkers &) = delete; - LineMarkers(LineMarkers &&) = delete; - void operator=(const LineMarkers &) = delete; - void operator=(LineMarkers &&) = delete; - ~LineMarkers() override; void Init() override; void InsertLine(Sci::Line line) override; void InsertLines(Sci::Line line, Sci::Line lines) override; @@ -78,12 +66,6 @@ class LineLevels : public PerLine { public: LineLevels() { } - // Deleted so LineLevels objects can not be copied. - LineLevels(const LineLevels &) = delete; - LineLevels(LineLevels &&) = delete; - void operator=(const LineLevels &) = delete; - void operator=(LineLevels &&) = delete; - ~LineLevels() override; void Init() override; void InsertLine(Sci::Line line) override; void InsertLines(Sci::Line line, Sci::Line lines) override; @@ -100,18 +82,12 @@ class LineState : public PerLine { public: LineState() { } - // Deleted so LineState objects can not be copied. - LineState(const LineState &) = delete; - LineState(LineState &&) = delete; - void operator=(const LineState &) = delete; - void operator=(LineState &&) = delete; - ~LineState() override; void Init() override; void InsertLine(Sci::Line line) override; void InsertLines(Sci::Line line, Sci::Line lines) override; void RemoveLine(Sci::Line line) override; - int SetLineState(Sci::Line line, int state); + int SetLineState(Sci::Line line, int state, Sci::Line lines); int GetLineState(Sci::Line line); Sci::Line GetMaxLineState() const noexcept; }; @@ -121,12 +97,8 @@ class LineAnnotation : public PerLine { public: LineAnnotation() { } - // Deleted so LineAnnotation objects can not be copied. - LineAnnotation(const LineAnnotation &) = delete; - LineAnnotation(LineAnnotation &&) = delete; - void operator=(const LineAnnotation &) = delete; - void operator=(LineAnnotation &&) = delete; - ~LineAnnotation() override; + + [[nodiscard]] bool Empty() const noexcept; void Init() override; void InsertLine(Sci::Line line) override; void InsertLines(Sci::Line line, Sci::Line lines) override; @@ -151,12 +123,6 @@ class LineTabstops : public PerLine { public: LineTabstops() { } - // Deleted so LineTabstops objects can not be copied. - LineTabstops(const LineTabstops &) = delete; - LineTabstops(LineTabstops &&) = delete; - void operator=(const LineTabstops &) = delete; - void operator=(LineTabstops &&) = delete; - ~LineTabstops() override; void Init() override; void InsertLine(Sci::Line line) override; void InsertLines(Sci::Line line, Sci::Line lines) override; diff --git a/scintilla/src/Platform.h b/scintilla/src/Platform.h index ce04d9b220..b0d12888d8 100644 --- a/scintilla/src/Platform.h +++ b/scintilla/src/Platform.h @@ -319,7 +319,7 @@ struct ListOptions { class ListBox : public Window { public: ListBox() noexcept; - virtual ~ListBox() noexcept override; + ~ListBox() noexcept override; static std::unique_ptr Allocate(); virtual void SetFont(const Font *font)=0; diff --git a/scintilla/src/PositionCache.cxx b/scintilla/src/PositionCache.cxx index bedc843df9..d36e3e6295 100644 --- a/scintilla/src/PositionCache.cxx +++ b/scintilla/src/PositionCache.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include "ScintillaTypes.h" #include "ScintillaMessages.h" @@ -86,11 +87,12 @@ LineLayout::~LineLayout() { void LineLayout::Resize(int maxLineLength_) { if (maxLineLength_ > maxLineLength) { Free(); - chars = std::make_unique(maxLineLength_ + 1); - styles = std::make_unique(maxLineLength_ + 1); + const size_t lineAllocation = maxLineLength_ + 1; + chars = std::make_unique(lineAllocation); + styles = std::make_unique(lineAllocation); // Extra position allocated as sometimes the Windows // GetTextExtentExPoint API writes an extra element. - positions = std::make_unique(maxLineLength_ + 1 + 1); + positions = std::make_unique(lineAllocation + 1); if (bidiData) { bidiData->Resize(maxLineLength_); } @@ -114,6 +116,10 @@ void LineLayout::Free() noexcept { bidiData.reset(); } +void LineLayout::ClearPositions() { + std::fill(&positions[0], &positions[maxLineLength + 2], 0.0f); +} + void LineLayout::Invalidate(ValidLevel validity_) noexcept { if (validity > validity_) validity = validity_; @@ -185,9 +191,10 @@ int LineLayout::SubLineFromPosition(int posInLine, PointEnd pe) const noexcept { return lines - 1; } -void LineLayout::SetLineStart(int line, int start) { - if ((line >= lenLineStarts) && (line != 0)) { - const int newMaxLines = line + 20; +void LineLayout::AddLineStart(Sci::Position start) { + lines++; + if (lines >= lenLineStarts) { + const int newMaxLines = lines + 20; std::unique_ptr newLineStarts = std::make_unique(newMaxLines); if (lenLineStarts) { std::copy(lineStarts.get(), lineStarts.get() + lenLineStarts, newLineStarts.get()); @@ -195,7 +202,7 @@ void LineLayout::SetLineStart(int line, int start) { lineStarts = std::move(newLineStarts); lenLineStarts = newMaxLines; } - lineStarts[line] = start; + lineStarts[lines] = static_cast(start); } void LineLayout::SetBracesHighlight(Range rangeLine, const Sci::Position braces[], @@ -298,6 +305,15 @@ Point LineLayout::PointFromPosition(int posInLine, int lineHeight, PointEnd pe) return pt; } +XYPOSITION LineLayout::XInLine(Sci::Position index) const noexcept { + // For positions inside line return value from positions + // For positions after line return last position + 1.0 + if (index <= numCharsInLine) { + return positions[index]; + } + return positions[numCharsInLine] + 1.0; +} + int LineLayout::EndLineStyle() const noexcept { return styles[numCharsBeforeEOL > 0 ? numCharsBeforeEOL-1 : 0]; } @@ -382,7 +398,7 @@ constexpr bool GraphicASCII(char ch) noexcept { return ch >= ' ' && ch <= '~'; } -bool AllGraphicASCII(std::string_view text) noexcept { +bool AllGraphicASCII(std::string_view text) { return std::all_of(text.cbegin(), text.cend(), GraphicASCII); } @@ -661,11 +677,10 @@ void BreakFinder::Insert(Sci::Position val) { } } -BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart_, +BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart, XYPOSITION xStart, BreakFor breakFor, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw) : ll(ll_), lineRange(lineRange_), - posLineStart(posLineStart_), nextBreak(static_cast(lineRange_.start)), saeCurrentPos(0), saeNext(0), @@ -740,7 +755,7 @@ TextSegment BreakFinder::Next() { const unsigned char ch = chars[0]; if (!UTF8IsAscii(ch) && encodingFamily != EncodingFamily::eightBit) { if (encodingFamily == EncodingFamily::unicode) { - charWidth = UTF8DrawBytes(reinterpret_cast(chars), static_cast(lineRange.end - nextBreak)); + charWidth = UTF8DrawBytes(chars, lineRange.end - nextBreak); } else { charWidth = pdoc->DBCSDrawBytes(std::string_view(chars, lineRange.end - nextBreak)); } @@ -799,6 +814,49 @@ bool BreakFinder::More() const noexcept { return (subBreak >= 0) || (nextBreak < lineRange.end); } +class PositionCacheEntry { + uint16_t styleNumber; + uint16_t len; + uint16_t clock; + std::unique_ptr positions; +public: + PositionCacheEntry() noexcept; + // Copy constructor not currently used, but needed for being element in std::vector. + PositionCacheEntry(const PositionCacheEntry &); + PositionCacheEntry(PositionCacheEntry &&) noexcept = default; + // Deleted so PositionCacheEntry objects can not be assigned. + void operator=(const PositionCacheEntry &) = delete; + void operator=(PositionCacheEntry &&) = delete; + ~PositionCacheEntry(); + void Set(unsigned int styleNumber_, std::string_view sv, const XYPOSITION *positions_, uint16_t clock_); + void Clear() noexcept; + bool Retrieve(unsigned int styleNumber_, std::string_view sv, XYPOSITION *positions_) const noexcept; + static size_t Hash(unsigned int styleNumber_, std::string_view sv) noexcept; + bool NewerThan(const PositionCacheEntry &other) const noexcept; + void ResetClock() noexcept; +}; + +class PositionCache : public IPositionCache { + std::vector pces; + std::mutex mutex; + uint16_t clock; + bool allClear; +public: + PositionCache(); + // Deleted so LineAnnotation objects can not be copied. + PositionCache(const PositionCache &) = delete; + PositionCache(PositionCache &&) = delete; + void operator=(const PositionCache &) = delete; + void operator=(PositionCache &&) = delete; + ~PositionCache() override = default; + + void Clear() noexcept override; + void SetSize(size_t size_) override; + size_t GetSize() const noexcept override; + void MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber, + std::string_view sv, XYPOSITION *positions, bool needsLocking) override; +}; + PositionCacheEntry::PositionCacheEntry() noexcept : styleNumber(0), len(0), clock(0) { } @@ -893,7 +951,7 @@ size_t PositionCache::GetSize() const noexcept { } void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber, - std::string_view sv, XYPOSITION *positions) { + std::string_view sv, XYPOSITION *positions, bool needsLocking) { const Style &style = vstyle.styles[styleNumber]; if (style.monospaceASCII) { if (AllGraphicASCII(sv)) { @@ -913,6 +971,10 @@ void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, uns // Two way associative: try two probe positions. const size_t hashValue = PositionCacheEntry::Hash(styleNumber, sv); probe = hashValue % pces.size(); + std::unique_lock guard(mutex, std::defer_lock); + if (needsLocking) { + guard.lock(); + } if (pces[probe].Retrieve(styleNumber, sv, positions)) { return; } @@ -930,6 +992,10 @@ void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, uns surface->MeasureWidths(fontStyle, sv, positions); if (probe < pces.size()) { // Store into cache + std::unique_lock guard(mutex, std::defer_lock); + if (needsLocking) { + guard.lock(); + } clock++; if (clock > 60000) { // Since there are only 16 bits for the clock, wrap it round and @@ -943,3 +1009,7 @@ void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, uns pces[probe].Set(styleNumber, sv, positions, clock); } } + +std::unique_ptr Scintilla::Internal::CreatePositionCache() { + return std::make_unique(); +} diff --git a/scintilla/src/PositionCache.h b/scintilla/src/PositionCache.h index 1cbc94495c..24e4e2d5a1 100644 --- a/scintilla/src/PositionCache.h +++ b/scintilla/src/PositionCache.h @@ -86,6 +86,7 @@ class LineLayout { void Resize(int maxLineLength_); void EnsureBidiData(); void Free() noexcept; + void ClearPositions(); void Invalidate(ValidLevel validity_) noexcept; Sci::Line LineNumber() const noexcept; bool CanHold(Sci::Line lineDoc, int lineLength_) const noexcept; @@ -96,13 +97,14 @@ class LineLayout { Range SubLineRange(int subLine, Scope scope) const noexcept; bool InLine(int offset, int line) const noexcept; int SubLineFromPosition(int posInLine, PointEnd pe) const noexcept; - void SetLineStart(int line, int start); + void AddLineStart(Sci::Position start); void SetBracesHighlight(Range rangeLine, const Sci::Position braces[], char bracesMatchStyle, int xHighlight, bool ignoreStyle); void RestoreBracesHighlight(Range rangeLine, const Sci::Position braces[], bool ignoreStyle); int FindBefore(XYPOSITION x, Range range) const noexcept; int FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const noexcept; Point PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const noexcept; + XYPOSITION XInLine(Sci::Position index) const noexcept; int EndLineStyle() const noexcept; }; @@ -163,28 +165,6 @@ class LineLayoutCache { Sci::Line linesOnScreen, Sci::Line linesInDoc); }; -class PositionCacheEntry { - uint16_t styleNumber; - uint16_t len; - uint16_t clock; - std::unique_ptr positions; -public: - PositionCacheEntry() noexcept; - // Copy constructor not currently used, but needed for being element in std::vector. - PositionCacheEntry(const PositionCacheEntry &); - PositionCacheEntry(PositionCacheEntry &&) noexcept = default; - // Deleted so PositionCacheEntry objects can not be assigned. - void operator=(const PositionCacheEntry &) = delete; - void operator=(PositionCacheEntry &&) = delete; - ~PositionCacheEntry(); - void Set(unsigned int styleNumber_, std::string_view sv, const XYPOSITION *positions_, uint16_t clock_); - void Clear() noexcept; - bool Retrieve(unsigned int styleNumber_, std::string_view sv, XYPOSITION *positions_) const noexcept; - static size_t Hash(unsigned int styleNumber_, std::string_view sv) noexcept; - bool NewerThan(const PositionCacheEntry &other) const noexcept; - void ResetClock() noexcept; -}; - class Representation { public: static constexpr size_t maxLength = 200; @@ -234,8 +214,7 @@ struct TextSegment { // Class to break a line of text into shorter runs at sensible places. class BreakFinder { const LineLayout *ll; - Range lineRange; - Sci::Position posLineStart; + const Range lineRange; int nextBreak; std::vector selAndEdge; unsigned int saeCurrentPos; @@ -257,7 +236,7 @@ class BreakFinder { Foreground = 2, ForegroundAndSelection = 3, }; - BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart_, + BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart, XYPOSITION xStart, BreakFor breakFor, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw); // Deleted so BreakFinder objects can not be copied. BreakFinder(const BreakFinder &) = delete; @@ -269,19 +248,18 @@ class BreakFinder { bool More() const noexcept; }; -class PositionCache { - std::vector pces; - uint16_t clock; - bool allClear; +class IPositionCache { public: - PositionCache(); - void Clear() noexcept; - void SetSize(size_t size_); - size_t GetSize() const noexcept; - void MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber, - std::string_view sv, XYPOSITION *positions); + virtual ~IPositionCache() = default; + virtual void Clear() noexcept = 0; + virtual void SetSize(size_t size_) = 0; + virtual size_t GetSize() const noexcept = 0; + virtual void MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber, + std::string_view sv, XYPOSITION *positions, bool needsLocking) = 0; }; +std::unique_ptr CreatePositionCache(); + } #endif diff --git a/scintilla/src/RESearch.cxx b/scintilla/src/RESearch.cxx index 15f3d62813..9a2981ffe4 100644 --- a/scintilla/src/RESearch.cxx +++ b/scintilla/src/RESearch.cxx @@ -305,6 +305,7 @@ constexpr unsigned char escapeValue(unsigned char ch) noexcept { case 'r': return '\r'; case 't': return '\t'; case 'v': return '\v'; + default: break; } return 0; } @@ -353,7 +354,7 @@ int RESearch::GetBackslashExpression( // I choose to interpret unexpected syntax in a logical way instead // of reporting errors. Otherwise, we can stick on, eg., PCRE behaviour. incr = 0; // Most of the time, will skip the char "naturally". - int c; + int c = 0; int result = -1; const unsigned char bsc = *pattern; if (!bsc) { @@ -433,16 +434,18 @@ int RESearch::GetBackslashExpression( const char *RESearch::Compile(const char *pattern, Sci::Position length, bool caseSensitive, bool posix) noexcept { char *mp=nfa; /* nfa pointer */ - char *lp; /* saved pointer */ + char *lp=nullptr; /* saved pointer */ char *sp=nfa; /* another one */ - char *mpMax = mp + MAXNFA - BITBLK - 10; + const char * mpMax = mp + MAXNFA - BITBLK - 10; int tagi = 0; /* tag stack index */ int tagc = 1; /* actual tag count */ - int n; - char mask; /* xor mask -CCL/NCL */ - int c1, c2, prevChar; + int n = 0; + char mask = 0; /* xor mask -CCL/NCL */ + int c1 = 0; + int c2 = 0; + int prevChar = 0; if (!pattern || !length) { if (sta) @@ -753,7 +756,7 @@ const char *RESearch::Compile(const char *pattern, Sci::Position length, bool ca * */ int RESearch::Execute(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp) { - unsigned char c; + unsigned char c = 0; Sci::Position ep = NOTFOUND; char *ap = nfa; @@ -841,12 +844,14 @@ int RESearch::Execute(const CharacterIndexer &ci, Sci::Position lp, Sci::Positio #define CCLSKIP 34 /* [CLO] CCL 32 bytes END */ Sci::Position RESearch::PMatch(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp, char *ap) { - int op, c, n; - Sci::Position e; /* extra pointer for CLO */ - Sci::Position bp; /* beginning of subpat... */ - Sci::Position ep; /* ending of subpat... */ - Sci::Position are; /* to save the line ptr. */ - Sci::Position llp; /* lazy lp for LCLO */ + int op = 0; + int c = 0; + int n = 0; + Sci::Position e = 0; /* extra pointer for CLO */ + Sci::Position bp = 0; /* beginning of subpat... */ + Sci::Position ep = 0; /* ending of subpat... */ + Sci::Position are = 0; /* to save the line ptr. */ + Sci::Position llp = 0; /* lazy lp for LCLO */ while ((op = *ap++) != END) switch (op) { diff --git a/scintilla/src/RunStyles.cxx b/scintilla/src/RunStyles.cxx index e9d3f6e4ec..5985bc960f 100644 --- a/scintilla/src/RunStyles.cxx +++ b/scintilla/src/RunStyles.cxx @@ -31,9 +31,9 @@ using namespace Scintilla::Internal; // Find the first run at a position template DISTANCE RunStyles::RunFromPosition(DISTANCE position) const noexcept { - DISTANCE run = starts->PartitionFromPosition(position); + DISTANCE run = starts.PartitionFromPosition(position); // Go to first element with this position - while ((run > 0) && (position == starts->PositionFromPartition(run-1))) { + while ((run > 0) && (position == starts.PositionFromPartition(run-1))) { run--; } return run; @@ -43,26 +43,26 @@ DISTANCE RunStyles::RunFromPosition(DISTANCE position) const no template DISTANCE RunStyles::SplitRun(DISTANCE position) { DISTANCE run = RunFromPosition(position); - const DISTANCE posRun = starts->PositionFromPartition(run); + const DISTANCE posRun = starts.PositionFromPartition(run); if (posRun < position) { STYLE runStyle = ValueAt(position); run++; - starts->InsertPartition(run, position); - styles->InsertValue(run, 1, runStyle); + starts.InsertPartition(run, position); + styles.InsertValue(run, 1, runStyle); } return run; } template void RunStyles::RemoveRun(DISTANCE run) { - starts->RemovePartition(run); - styles->DeleteRange(run, 1); + starts.RemovePartition(run); + styles.DeleteRange(run, 1); } template void RunStyles::RemoveRunIfEmpty(DISTANCE run) { - if ((run < starts->Partitions()) && (starts->Partitions() > 1)) { - if (starts->PositionFromPartition(run) == starts->PositionFromPartition(run+1)) { + if ((run < starts.Partitions()) && (starts.Partitions() > 1)) { + if (starts.PositionFromPartition(run) == starts.PositionFromPartition(run+1)) { RemoveRun(run); } } @@ -70,8 +70,9 @@ void RunStyles::RemoveRunIfEmpty(DISTANCE run) { template void RunStyles::RemoveRunIfSameAsPrevious(DISTANCE run) { - if ((run > 0) && (run < starts->Partitions())) { - if (styles->ValueAt(run-1) == styles->ValueAt(run)) { + if ((run > 0) && (run < starts.Partitions())) { + const DISTANCE runBefore = run - 1; + if (styles.ValueAt(runBefore) == styles.ValueAt(run)) { RemoveRun(run); } } @@ -79,33 +80,29 @@ void RunStyles::RemoveRunIfSameAsPrevious(DISTANCE run) { template RunStyles::RunStyles() { - starts = std::make_unique>(8); - styles = std::make_unique>(); - styles->InsertValue(0, 2, 0); -} - -template -RunStyles::~RunStyles() { + starts = Partitioning(8); + styles = SplitVector