following code set ll->styles[numCharsInLine] to style value for last EOL character.
// if (ll->validity == LineLayout::ValidLevel::invalid)
const int lineLength = static_cast<int>(posLineEnd - posLineStart);
const int numCharsBeforeEOL = static_cast<int>(model.pdoc->LineEnd(line) - posLineStart);
const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;
ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
assuming posLineStart is zero, i.e. first line.
in validating loop inside if (ll->validity == LineLayout::ValidLevel::checkTextAndStyle) block,
the statement styleByte = model.pdoc->StyleIndexAt(charInDoc); gives different results:
styleByte = model.pdoc->StyleIndexAt(lineLength - 1);, i.e. styleByteLast.styleByte = model.pdoc->StyleIndexAt(numCharsBeforeEOL - 1);, i.e. style for last character before EOL character, which has nothing related to styleByteLast, unless it's colored same with EOL characters, like if (sc.atLineStart) { sc.SetState(SCE_XXX_DEFAULT); }. so the following test mostly failed, which make the cache useless.allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
a possible fix:
+ if (!vstyle.viewEOL) {
+ styleByte = model.pdoc->StyleIndexAt(numCharsInLine + posLineStart);
+ }
allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
What is the actual problem here? Is the cached line incorrectly treated as valid or invalid under some circumstances?
It's incorrectly treated as invalid.
Assume caret line is very long, caret blinking and word wrapping is enabled, this cause the application unresponsive for very long (infinite?) time, without enough idle time to wrap other lines, because cache for caret line been treated as "invalid", then measure widths again and again, the
while (bfLayout.More())loop.Last edit: Zufu Liu 2020-08-16
A reproducer:
Open your Visual Studio installer, click modify button, click Installation Location tab, open the download cache folder, for me this is
D:\Program Files\Microsoft Visual Studio\Packages, there is a _Instances sub folder, with contains folders named with random hashes.open one of them, there is a large (based on how many components you installed) catalog.json file.
Run following Python script to produce pretty.json and catalog2.json, then open the three JSON files with SciTE, catalog2.json is always unresponsive, while pretty.json can be opened quickly, and catalog2.json can be opened with some time.
A simple reproducer.
Add following line in EditView::LayoutLine()
(on Windows) add lines in WinMain:
build Scintilla and SciTE, open SciTEUser.properties, add
cache.layout=2(SC_CACHE_PAGE).then open ILexer.h, delete last 'H' on line 9
#define ILEXER_H, watch console output; then scroll down, watch console output. you will see a lots of logs likeline=58, allSame=0, styleByte=10 / 0.using variables defined in
if (ll->validity == LineLayout::ValidLevel::invalid)block:this check
is equivalent to following when viewEOL is false.
My suggested change (set styleByte to style for first EOL character):
Does not fix buggy lexers that color CRLF with different styles, several such lexers had been fixed in the past.
Two patches fixed this bug.
first change:
the eolFilled only tested for viewEOL is false, when viewEOL is true, whole line is already tested.
second change:
, set styleByteLast to style for last EOL character when viewEOL is true and first EOL character when viewEOL is false.
fix-opt.diff optimized the loop with xor and bitwise or, and added a separate loop for someStylesForceCase.
At 2020-08-17 08:51:47, "Zufu Liu" zufuliu@users.sourceforge.net wrote:
A simple reproducer.
Add following line in EditView::LayoutLine()
allSame=allSame&&(ll->styles[numCharsInLine]==styleByte);// For eolFilledprintf("line=%zd, allSame=%d, styleByte=%d / %d\n",line+1,allSame,styleByte,ll->styles[numCharsInLine]);
(on Windows) add lines in WinMain:
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCE,LPSTR,int){#if 1if(AttachConsole(ATTACH_PARENT_PROCESS)){freopen("CONOUT$","w",stdout);freopen("CONOUT$","w",stderr);fprintf(stdout,"\n%s:%d %s\n",FILE,LINE,FUNCTION);}#endif
build Scintilla and SciTE, open SciTEUser.properties, add cache.layout=2 (SC_CACHE_PAGE).
then open ILexer.h, delete last 'H' on line 9 #define ILEXER_H, watch console output; then scroll down, watch console output. you will see a lots of logs like line=58, allSame=0, styleByte=10 / 0.
using variables defined in if (ll->validity == LineLayout::ValidLevel::invalid) block:
constintlineLength=static_cast<int>(posLineEnd-posLineStart);constintnumCharsBeforeEOL=static_cast<int>(model.pdoc->LineEnd(line)-posLineStart);constintnumCharsInLine=(vstyle.viewEOL)?lineLength:numCharsBeforeEOL;constunsignedcharstyleByteLast=(lineLength>0)?ll->styles[lineLength-1]:0;ll->styles[numCharsInLine]=styleByteLast;// For eolFilled</int></int>
this check
allSame=allSame&&(ll->styles[numCharsInLine]==styleByte);
is equivalent to following when viewEOL is false.
allSame=allSame&&(ll->styles[numCharsBeforeEOL]==ll->styles[numCharsBeforeEOL-1]);// ll->styles[numCharsBeforeEOL] = styleByteLast = style for last EOL character// ll->styles[numCharsBeforeEOL - 1] = style for last character before first EOL character// so the check most of the time failed
My suggested change (set styleByte to style for first EOL character):
Does not fix buggy lexers that color CRLF with different styles, several such lexers had been fixed in the past.
[bugs:#2197] Off-by-one Error in EditView::LayoutLine()
Status: open
Group: Bug
Created: Sun Aug 16, 2020 05:35 PM UTC by Zufu Liu
Last Updated: Sun Aug 16, 2020 10:59 PM UTC
Owner: Neil Hodgson
following code set ll->styles[numCharsInLine] to style value for last EOL character.
// if (ll->validity == LineLayout::ValidLevel::invalid)constintlineLength=static_cast<int>(posLineEnd-posLineStart);constintnumCharsBeforeEOL=static_cast<int>(model.pdoc->LineEnd(line)-posLineStart);constintnumCharsInLine=(vstyle.viewEOL)?lineLength:numCharsBeforeEOL;constunsignedcharstyleByteLast=(lineLength>0)?ll->styles[lineLength-1]:0;ll->styles[numCharsInLine]=styleByteLast;// For eolFilled</int></int>
assuming posLineStart is zero, i.e. first line.
in validating loop inside if (ll->validity == LineLayout::ValidLevel::checkTextAndStyle) block,
the statement styleByte = model.pdoc->StyleIndexAt(charInDoc); gives different results:
allSame=allSame&&(ll->styles[numCharsInLine]==styleByte);// For eolFilled
a possible fix:
allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/scintilla/bugs/2197/
To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/
Related
Bugs:
#2197A minimal change would be to mirror the effects from the ValidLevel::invalid case back up into the ValidLevel::checkTextAndStyle case, checking that the styles[numCharsInLine] eolfilled byte is the same as the last style byte of the line:
(the non-empty check appears to never fail)
This seems less stable to me with eolfilled lines potentially appearing different when view EOL is on/off. It could also be different to previous behaviour.
The change works.
After the change, styleByte can be moved into the loop, though the loop itself can be optimized with RangePointer + memcmp.
Committed with [aa1c85], [c10c2f].
I am hesitant to potentially move the gap for read operations.
Related
Commit: [aa1c85]
Commit: [c10c2f]
There need another change to fix repeated wrap when allSame is true.
The time by moving gap is smaller than changing RangePointer to returns two pointers like:
What's wrong with LayoutLine-wrap.diff:
Committed as [41fb5d].
Related
Commit: [41fb5d]