aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2015-11-11 19:14:30 +1100
committerNeil <nyamatongwe@gmail.com>2015-11-11 19:14:30 +1100
commit23460f666bf34479850000eed40ba0ea38ca6665 (patch)
tree1c199ed2a3adb52f3cb6790b39532235e66e38f2
parent3684fe443af3fcc0e5ad8ab674203a58c4ccaac3 (diff)
downloadscintilla-mirror-23460f666bf34479850000eed40ba0ea38ca6665.tar.gz
Implemented idle styling. This allows painting without first styling all visible
text then styling in the background using idle-time.
-rw-r--r--cocoa/ScintillaCocoa.mm10
-rw-r--r--doc/ScintillaDoc.html21
-rw-r--r--doc/ScintillaHistory.html6
-rw-r--r--include/Scintilla.h6
-rw-r--r--include/Scintilla.iface12
-rw-r--r--src/Document.cxx28
-rw-r--r--src/Document.h2
-rw-r--r--src/Editor.cxx80
-rw-r--r--src/Editor.h6
9 files changed, 161 insertions, 10 deletions
diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm
index 8b95aae0e..982ca0423 100644
--- a/cocoa/ScintillaCocoa.mm
+++ b/cocoa/ScintillaCocoa.mm
@@ -1801,13 +1801,15 @@ void ScintillaCocoa::WillDraw(NSRect rect)
{
RefreshStyleData();
PRectangle rcWillDraw = NSRectToPRectangle(rect);
- int positionAfterRect = PositionAfterArea(rcWillDraw);
- pdoc->EnsureStyledTo(positionAfterRect);
+ const int posAfterArea = PositionAfterArea(rcWillDraw);
+ const int posAfterMax = PositionAfterMaxStyling(posAfterArea, true);
+ pdoc->StyleToAdjustingLineDuration(posAfterMax);
+ StartIdleStyling(posAfterMax < posAfterArea);
NotifyUpdateUI();
if (WrapLines(wsVisible)) {
// Wrap may have reduced number of lines so more lines may need to be styled
- positionAfterRect = PositionAfterArea(rcWillDraw);
- pdoc->EnsureStyledTo(positionAfterRect);
+ const int posAfterAreaWrapped = PositionAfterArea(rcWillDraw);
+ pdoc->EnsureStyledTo(posAfterAreaWrapped);
// The wrapping process has changed the height of some lines so redraw all.
Redraw();
}
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html
index e77c064c3..34722f259 100644
--- a/doc/ScintillaDoc.html
+++ b/doc/ScintillaDoc.html
@@ -82,7 +82,7 @@
<h1>Scintilla Documentation</h1>
- <p>Last edited 31 July 2015 NH</p>
+ <p>Last edited 11 November 2015 NH</p>
<p>There is <a class="jump" href="Design.html">an overview of the internal design of
Scintilla</a>.<br />
@@ -2391,6 +2391,8 @@ struct Sci_TextToFind {
<a class="message" href="#SCI_SETSTYLING">SCI_SETSTYLING(int length, int style)</a><br />
<a class="message" href="#SCI_SETSTYLINGEX">SCI_SETSTYLINGEX(int length, const char
*styles)</a><br />
+ <a class="message" href="#SCI_SETIDLESTYLING">SCI_SETIDLESTYLING(int idleStyling)</a><br />
+ <a class="message" href="#SCI_GETIDLESTYLING">SCI_GETIDLESTYLING</a><br />
<a class="message" href="#SCI_SETLINESTATE">SCI_SETLINESTATE(int line, int value)</a><br />
<a class="message" href="#SCI_GETLINESTATE">SCI_GETLINESTATE(int line)</a><br />
<a class="message" href="#SCI_GETMAXLINESTATE">SCI_GETMAXLINESTATE</a><br />
@@ -2426,6 +2428,23 @@ struct Sci_TextToFind {
<code>SCI_STARTSTYLING</code> should be called before the first call to this.
</p>
+ <p><b id="SCI_SETIDLESTYLING">SCI_SETIDLESTYLING(int idleStyling)</b><br />
+ <b id="SCI_GETIDLESTYLING">SCI_GETIDLESTYLING</b><br />
+ By default, <code>SC_IDLESTYLING_NONE</code> (0),
+ syntax styling is performed for all the currently visible text before displaying it.
+ On very large files, this may make scrolling down slow.
+ With <code>SC_IDLESTYLING_TOVISIBLE</code> (1),
+ a small amount of styling is performed before display and then
+ further styling is performed incrementally in the background as an idle-time task.
+ This may result in the text initially appearing uncoloured and then, some time later, it is coloured.
+ Text after the currently visible portion may be styled in the background with <code>SC_IDLESTYLING_AFTERVISIBLE</code> (2).
+ To style both before and after the visible text in the background use <code>SC_IDLESTYLING_ALL</code> (3).
+ </p>
+ <p>
+ Since wrapping also needs to perform styling and also uses idle time, this setting has no effect when
+ the document is displayed wrapped.
+ </p>
+
<p><b id="SCI_SETLINESTATE">SCI_SETLINESTATE(int line, int value)</b><br />
<b id="SCI_GETLINESTATE">SCI_GETLINESTATE(int line)</b><br />
As well as the 8 bits of lexical state stored for each character there is also an integer
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html
index 9aea9b6e3..0639d28fb 100644
--- a/doc/ScintillaHistory.html
+++ b/doc/ScintillaHistory.html
@@ -497,6 +497,12 @@
Released 6 November 2015.
</li>
<li>
+ Allow painting without first styling all visible text then styling in the background
+ using idle-time. This helps performance when scrolling down in very large documents.
+ Can also incrementally style after the visible area to the end of the document so that
+ the document is already styled when the user scrolls to it.
+ </li>
+ <li>
On GTK+ on OS X, fix warning during destruction.
<a href="http://sourceforge.net/p/scintilla/bugs/1777/">Bug #1777</a>.
</li>
diff --git a/include/Scintilla.h b/include/Scintilla.h
index e3443409b..c985cea77 100644
--- a/include/Scintilla.h
+++ b/include/Scintilla.h
@@ -506,6 +506,12 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam,
#define SCI_WORDSTARTPOSITION 2266
#define SCI_WORDENDPOSITION 2267
#define SCI_ISRANGEWORD 2691
+#define SC_IDLESTYLING_NONE 0
+#define SC_IDLESTYLING_TOVISIBLE 1
+#define SC_IDLESTYLING_AFTERVISIBLE 2
+#define SC_IDLESTYLING_ALL 3
+#define SCI_SETIDLESTYLING 2692
+#define SCI_GETIDLESTYLING 2693
#define SC_WRAP_NONE 0
#define SC_WRAP_WORD 1
#define SC_WRAP_CHAR 2
diff --git a/include/Scintilla.iface b/include/Scintilla.iface
index ad50a2aaf..04fd767cd 100644
--- a/include/Scintilla.iface
+++ b/include/Scintilla.iface
@@ -1260,6 +1260,18 @@ fun int WordEndPosition=2267(position pos, bool onlyWordCharacters)
# Is the range start..end considered a word?
fun bool IsRangeWord=2691(position start, position end)
+enu IdleStyling=SC_IDLESTYLING_
+val SC_IDLESTYLING_NONE=0
+val SC_IDLESTYLING_TOVISIBLE=1
+val SC_IDLESTYLING_AFTERVISIBLE=2
+val SC_IDLESTYLING_ALL=3
+
+# Sets limits to idle styling.
+set void SetIdleStyling=2692(int idleStyling,)
+
+# Retrieve the limits to idle styling.
+get int GetIdleStyling=2693(,)
+
enu Wrap=SC_WRAP_
val SC_WRAP_NONE=0
val SC_WRAP_WORD=1
diff --git a/src/Document.cxx b/src/Document.cxx
index 9201e162f..65c63c138 100644
--- a/src/Document.cxx
+++ b/src/Document.cxx
@@ -109,6 +109,7 @@ Document::Document() {
useTabs = true;
tabIndents = true;
backspaceUnindents = false;
+ durationStyleOneLine = 0.00001;
matchesValid = false;
regex = 0;
@@ -1892,6 +1893,33 @@ void Document::EnsureStyledTo(int pos) {
}
}
+void Document::StyleToAdjustingLineDuration(int pos) {
+ // Place bounds on the duration used to avoid glitches spiking it
+ // and so causing slow styling or non-responsive scrolling
+ const double minDurationOneLine = 0.000001;
+ const double maxDurationOneLine = 0.0001;
+
+ // Alpha value for exponential smoothing.
+ // Most recent value contributes 25% to smoothed value.
+ const double alpha = 0.25;
+
+ const Sci_Position lineFirst = LineFromPosition(GetEndStyled());
+ ElapsedTime etStyling;
+ EnsureStyledTo(pos);
+ const double durationStyling = etStyling.Duration();
+ const Sci_Position lineLast = LineFromPosition(GetEndStyled());
+ if (lineLast >= lineFirst + 8) {
+ // Only adjust for styling multiple lines to avoid instability
+ const double durationOneLine = durationStyling / (lineLast - lineFirst);
+ durationStyleOneLine = alpha * durationOneLine + (1.0 - alpha) * durationStyleOneLine;
+ if (durationStyleOneLine < minDurationOneLine) {
+ durationStyleOneLine = minDurationOneLine;
+ } else if (durationStyleOneLine > maxDurationOneLine) {
+ durationStyleOneLine = maxDurationOneLine;
+ }
+ }
+}
+
void Document::LexerChanged() {
// Tell the watchers the lexer has changed.
for (std::vector<WatcherWithUserData>::iterator it = watchers.begin(); it != watchers.end(); ++it) {
diff --git a/src/Document.h b/src/Document.h
index ea8ddfbed..71a45879e 100644
--- a/src/Document.h
+++ b/src/Document.h
@@ -246,6 +246,7 @@ public:
bool useTabs;
bool tabIndents;
bool backspaceUnindents;
+ double durationStyleOneLine;
DecorationList decorations;
@@ -400,6 +401,7 @@ public:
bool SCI_METHOD SetStyles(Sci_Position length, const char *styles);
int GetEndStyled() const { return endStyled; }
void EnsureStyledTo(int pos);
+ void StyleToAdjustingLineDuration(int pos);
void LexerChanged();
int GetStyleClock() const { return styleClock; }
void IncrementStyleClock();
diff --git a/src/Editor.cxx b/src/Editor.cxx
index 6141b0628..a86f8c96e 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -173,6 +173,8 @@ Editor::Editor() {
paintAbandonedByStyling = false;
paintingAllText = false;
willRedrawAll = false;
+ idleStyling = SC_IDLESTYLING_NONE;
+ needIdleStyling = false;
modEventMask = SC_MODEVENTMASKALL;
@@ -919,7 +921,7 @@ void Editor::ScrollTo(int line, bool moveThumb) {
SetTopLine(topLineNew);
// Optimize by styling the view as this will invalidate any needed area
// which could abort the initial paint if discovered later.
- StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
+ StyleAreaBounded(GetClientRectangle(), true);
#ifndef UNDER_CE
// Perform redraw rather than scroll if many lines would be redrawn anyway.
if (performBlit) {
@@ -1692,7 +1694,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
paintAbandonedByStyling = false;
- StyleToPositionInView(PositionAfterArea(rcArea));
+ StyleAreaBounded(rcArea, false);
PRectangle rcClient = GetClientRectangle();
//Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
@@ -4893,6 +4895,8 @@ bool Editor::Idle() {
// No more wrapping
if (!wrapPending.NeedsWrap())
wrappingDone = true;
+ } else if (needIdleStyling) {
+ IdleStyling();
}
// Add more idle things to do here, but make sure idleDone is
@@ -4900,7 +4904,7 @@ bool Editor::Idle() {
// false will stop calling this idle function until SetIdle() is
// called again.
- idleDone = wrappingDone; // && thatDone && theOtherThingDone...
+ idleDone = wrappingDone && !needIdleStyling; // && thatDone && theOtherThingDone...
return !idleDone;
}
@@ -5003,12 +5007,71 @@ void Editor::StyleToPositionInView(Position pos) {
}
}
+int Editor::PositionAfterMaxStyling(int posMax, bool scrolling) const {
+ if ((idleStyling == SC_IDLESTYLING_NONE) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
+ // Both states do not limit styling
+ return posMax;
+ }
+
+ // Try to keep time taken by styling reasonable so interaction remains smooth.
+ // When scrolling, allow less time to ensure responsive
+ const double secondsAllowed = scrolling ? 0.005 : 0.02;
+
+ const int linesToStyle = Platform::Clamp(static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine),
+ 10, 0x10000);
+ const int stylingMaxLine = std::min(
+ static_cast<int>(pdoc->LineFromPosition(pdoc->GetEndStyled()) + linesToStyle),
+ pdoc->LinesTotal());
+ return std::min(static_cast<int>(pdoc->LineStart(stylingMaxLine)), posMax);
+}
+
+void Editor::StartIdleStyling(bool truncatedLastStyling) {
+ if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
+ if (pdoc->GetEndStyled() < pdoc->Length()) {
+ // Style remainder of document in idle time
+ needIdleStyling = true;
+ }
+ } else if (truncatedLastStyling) {
+ needIdleStyling = true;
+ }
+
+ if (needIdleStyling) {
+ SetIdle(true);
+ }
+}
+
+// Style for an area but bound the amount of styling to remain responsive
+void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
+ const int posAfterArea = PositionAfterArea(rcArea);
+ const int posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
+ if (posAfterMax < posAfterArea) {
+ // Idle styling may be performed before current visible area
+ // Style a bit now then style further in idle time
+ pdoc->StyleToAdjustingLineDuration(posAfterMax);
+ } else {
+ // Can style all wanted now.
+ StyleToPositionInView(posAfterArea);
+ }
+ StartIdleStyling(posAfterMax < posAfterArea);
+}
+
+void Editor::IdleStyling() {
+ const int posAfterArea = PositionAfterArea(GetClientRectangle());
+ const int endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ?
+ pdoc->Length() : posAfterArea;
+ const int posAfterMax = PositionAfterMaxStyling(endGoal, false);
+ pdoc->StyleToAdjustingLineDuration(posAfterMax);
+ if (pdoc->GetEndStyled() >= endGoal) {
+ needIdleStyling = false;
+ }
+}
+
void Editor::IdleWork() {
// Style the line after the modification as this allows modifications that change just the
// line of the modification to heal instead of propagating to the rest of the window.
- if (workNeeded.items & WorkNeeded::workStyle)
+ if (workNeeded.items & WorkNeeded::workStyle) {
StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
-
+ }
NotifyUpdateUI();
workNeeded.Reset();
}
@@ -6400,6 +6463,13 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
case SCI_ISRANGEWORD:
return pdoc->IsWordAt(static_cast<int>(wParam), static_cast<int>(lParam));
+
+ case SCI_SETIDLESTYLING:
+ idleStyling = static_cast<int>(wParam);
+ break;
+
+ case SCI_GETIDLESTYLING:
+ return idleStyling;
case SCI_SETWRAPMODE:
if (vs.SetWrapState(static_cast<int>(wParam))) {
diff --git a/src/Editor.h b/src/Editor.h
index 7b88cbab0..0d788b5a8 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -234,6 +234,8 @@ protected: // ScintillaBase subclass needs access to much of Editor
bool paintingAllText;
bool willRedrawAll;
WorkNeeded workNeeded;
+ int idleStyling;
+ bool needIdleStyling;
int modEventMask;
@@ -525,6 +527,10 @@ protected: // ScintillaBase subclass needs access to much of Editor
int PositionAfterArea(PRectangle rcArea) const;
void StyleToPositionInView(Position pos);
+ int PositionAfterMaxStyling(int posMax, bool scrolling) const;
+ void StartIdleStyling(bool truncatedLastStyling);
+ void StyleAreaBounded(PRectangle rcArea, bool scrolling);
+ void IdleStyling();
virtual void IdleWork();
virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo=0);