diff options
| author | Neil <nyamatongwe@gmail.com> | 2026-03-30 18:30:55 +1100 |
|---|---|---|
| committer | Neil <nyamatongwe@gmail.com> | 2026-03-30 18:30:55 +1100 |
| commit | f07caccbf33acd188cbe4961ece30251102351cc (patch) | |
| tree | 9a7b573d41923b77229e28386164d05e9d710fff | |
| parent | a00464fbff54f486efa3ee103948e3526548fc46 (diff) | |
| download | scintilla-mirror-f07caccbf33acd188cbe4961ece30251102351cc.tar.gz | |
Feature [feature-requests:#1582]. Improve performance of Document::SetStyles.
Report changed range for Document::SetStyleFor.
| -rw-r--r-- | src/CellBuffer.cxx | 103 | ||||
| -rw-r--r-- | src/CellBuffer.h | 24 | ||||
| -rw-r--r-- | src/Document.cxx | 25 |
3 files changed, 107 insertions, 45 deletions
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx index 3e9deb934..3d4050633 100644 --- a/src/CellBuffer.cxx +++ b/src/CellBuffer.cxx @@ -439,35 +439,90 @@ const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci: return data; } -bool CellBuffer::SetStyleAt(Sci::Position position, char styleValue) noexcept { - if (!hasStyles) { - return false; - } - const char curVal = style.ValueAt(position); - if (curVal != styleValue) { - style.SetValueAt(position, styleValue); - return true; - } else { - return false; +namespace { + +ChangedRange CopyBytes(char *p, const char *styles, Sci::Position length, Sci::Position offset) noexcept { + for (Sci::Position start = 0; start < length; start++) { + if (p[start] != styles[start]) { + for (Sci::Position end = length - 1; end >= 0; end--) { + if (p[end] != styles[end]) { + memcpy(p+start, styles+start, end-start+1); + return { start + offset, end + offset }; + } + } + } } + return {}; } -bool CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept { - if (!hasStyles) { - return false; - } - bool changed = false; - PLATFORM_ASSERT(lengthStyle == 0 || - (lengthStyle > 0 && lengthStyle + position <= style.Length())); - while (lengthStyle--) { - const char curVal = style.ValueAt(position); - if (curVal != styleValue) { - style.SetValueAt(position, styleValue); - changed = true; +ChangedRange SetBytes(char *p, char style, Sci::Position length, Sci::Position offset) noexcept { + for (Sci::Position start = 0; start < length; start++) { + if (p[start] != style) { + for (Sci::Position end = length - 1; end >= 0; end--) { + if (p[end] != style) { + memset(p+start, style, end-start+1); + return { start + offset, end + offset }; + } + } } - position++; } - return changed; + return {}; +} + +struct Lengths { + Sci::Position length1; + Sci::Position length2; +}; + +Lengths SplitUpdate(const SplitVector<char> &style, Sci::Position position, Sci::Position length) noexcept { + length = std::min(length, style.Length() - position); + + // Divide length into two parts if it overlaps the gap + const Sci::Position part1Length = style.GapPosition(); + + // If all after gap then place in length1 to avoid second call + if (position >= part1Length) { + return { length, 0 }; + } + // If all before gap then place in length1 + const Sci::Position beforeGap = part1Length - position; + if (length <= beforeGap) { + return { length, 0 }; + } + // Some before gap and some after gap so put portion in length1 and rest in length2 + return { beforeGap, length - beforeGap }; +} + +} + +ChangedRange CellBuffer::SetStyles(Sci::Position position, const char *styles, Sci::Position length) noexcept { + if (!hasStyles || (position < 0) || (length <= 0)) { + return {}; + } + + const Lengths lengths = SplitUpdate(style, position, length); + ChangedRange cr = CopyBytes(&style[position], styles, lengths.length1, position); + if (lengths.length2) { + const ChangedRange cr2 = CopyBytes(&style[position + lengths.length1], styles + lengths.length1, + lengths.length2, position + lengths.length1); + cr.Merge(cr2); + } + return cr; +} + +ChangedRange CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept { + if (!hasStyles || (position < 0) || (lengthStyle <= 0)) { + return {}; + } + + const Lengths lengths = SplitUpdate(style, position, lengthStyle); + ChangedRange cr = SetBytes(&style[position], styleValue, lengths.length1, position); + if (lengths.length2) { + const ChangedRange cr2 = SetBytes(&style[position + lengths.length1], styleValue, + lengths.length2, position + lengths.length1); + cr.Merge(cr2); + } + return cr; } // The char* returned is to an allocation owned by the undo history diff --git a/src/CellBuffer.h b/src/CellBuffer.h index 1b035597a..e48eded4d 100644 --- a/src/CellBuffer.h +++ b/src/CellBuffer.h @@ -66,6 +66,24 @@ struct SplitView { } }; +struct ChangedRange { + Sci::Position start = Sci::invalidPosition; + Sci::Position end = Sci::invalidPosition; + ChangedRange() noexcept = default; + ChangedRange(Sci::Position start_, Sci::Position end_) noexcept : start(start_), end(end_) {} + [[nodiscard]] bool Empty() const noexcept { + return start < 0; + } + void Merge(const ChangedRange &cr2) noexcept { + if (cr2.start >= 0) { + if (start < 0) { + *this = cr2; + } else { + end = cr2.end; + } + } + } +}; /** * Holder for an expandable array of characters that supports undo and line markers. @@ -141,9 +159,9 @@ public: const char *InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence); /// Setting styles for positions outside the range of the buffer is safe and has no effect. - /// @return true if the style of a character is changed. - bool SetStyleAt(Sci::Position position, char styleValue) noexcept; - bool SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept; + /// @return range where style of characters changed. + ChangedRange SetStyles(Sci::Position position, const char *styles, Sci::Position length) noexcept; + ChangedRange SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept; const char *DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence); diff --git a/src/Document.cxx b/src/Document.cxx index ab4da0179..d4754233e 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -2571,10 +2571,10 @@ bool SCI_METHOD Document::SetStyleFor(Sci_Position length, char style) { return false; } enteredStyling++; - const Sci::Position prevEndStyled = endStyled; - if (cb.SetStyleFor(endStyled, length, style)) { + const ChangedRange cr = cb.SetStyleFor(endStyled, length, style); + if (!cr.Empty()) { const DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User, - prevEndStyled, length); + cr.start, cr.end - cr.start + 1); NotifyModified(mh); } endStyled += length; @@ -2587,22 +2587,11 @@ bool SCI_METHOD Document::SetStyles(Sci_Position length, const char *styles) { return false; } enteredStyling++; - bool didChange = false; - Sci::Position startMod = 0; - Sci::Position endMod = 0; - for (int iPos = 0; iPos < length; iPos++, endStyled++) { - PLATFORM_ASSERT(endStyled < Length()); - if (cb.SetStyleAt(endStyled, styles[iPos])) { - if (!didChange) { - startMod = endStyled; - } - didChange = true; - endMod = endStyled; - } - } - if (didChange) { + const ChangedRange cr = cb.SetStyles(endStyled, styles, length); + endStyled += length; + if (!cr.Empty()) { const DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User, - startMod, endMod - startMod + 1); + cr.start, cr.end - cr.start + 1); NotifyModified(mh); } enteredStyling--; |
