-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
prevent negative code folding level in lexer #224
Comments
Complex fix may like Ruby lexer that guards each decrement. |
Folded groups of line comments may be a challenge, too (cf. #56 (comment)): |
Due to Notepad2 always move back one line before call fold method, https://github.com/zufuliu/notepad2/blob/main/scintilla/lexlib/LexerBase.cxx#L83 add following block make SciTE works too: void SCI_METHOD LexerBash::Fold(Sci_PositionU startPos_, Sci_Position length, int initStyle, IDocument *pAccess) {
if(!options.fold)
return;
{
Sci_Position lineCurrent = pAccess->LineFromPosition(startPos_);
// Move back one line in case deletion wrecked current line fold state
if (lineCurrent != 0) {
lineCurrent--;
const Sci_Position newStartPos = pAccess->LineStart(lineCurrent);
length += startPos_ - newStartPos;
startPos_ = newStartPos;
initStyle = 0;
if (startPos_ != 0) {
initStyle = pAccess->StyleAt(startPos_ - 1);
}
}
}
LexAccessor styler(pAccess); |
code come from |
Unbalanced braces are generally incorrect code and should be fixed. While the current 'messed up ugly' display doesn't strongly indicate the problem, hiding that anything is wrong seems counterproductive to improving the code. |
patch added "Move back one line" block (don't known better fix), and convert if-else to switch. consecutive line comment folding block: if (!IsCommentLine(lineCurrent - 1, styler)
&& IsCommentLine(lineCurrent + 1, styler))
levelCurrent++;
else if (IsCommentLine(lineCurrent - 1, styler)
&& !IsCommentLine(lineCurrent + 1, styler))
levelCurrent--; can be simplified as (not in the patch) levelCurrent += IsCommentLine(lineCurrent + 1, styler) - IsCommentLine(lineCurrent - 1, styler); |
Simplified backtrack based on
|
Correct |
It would be better to strongly indicate where folding goes negative, with a new visual symbol in the folding margin, to guide the user to fixing the error. It's not obvious what language zufuliu/notepad4#745 is in. |
Most likely JavaScript, but it applies to other lexer with brace or word based code folding. function f1() {
if () {
// code
} else {
// code
}
}
function f2() {
} |
Committed the change to the
It doesn't look like the Issues should contain enough information to be reproducible. |
From the book review it sounds more like the self-help I've seen dispensed to sufferers of writer's block; namely, always leave some trivial task unfinished so you start feeling productive as soon as you return to complete it.
I think it really is "[m]ost likely" Notepad2's dedicated JavaScript lexer, which folds line comments forward and back: https://github.com/zufuliu/notepad2/blob/b71af2f18599562dc4767324c5c8e030c3039d8b/scintilla/lexers/LexJavaScript.cxx#L680-L681 |
The ugly display bug in Notepad2 is my failure (after change Bug version Notepad2 (negative folding level with Old version Notepad2 (negative folding level without SciTE current (negative folding level), same as old Notepad2: Notepad2 current (non-negative folding level with |
There are two paths here: keeping negative levels and discarding negative levels. I see advantage to keeping and you see advantage to discarding. The currently proposed discarding approach requires changes to each lexer then understanding and cooperation from lexer authors and maintainers. An alternative implementation would move the discard choice to lexer-independent code as a mode that could be selected. With discard-negative-fold mode, for negative lines:
The delta would be used for some calls but there would also be access to the raw fold levels. The delta could be recomputed during operations, such as the margin drawing code if it finds negative levels. It could also be stored as a simple sorted |
I'm sorry, but I don't get how the delta approach works, just feel change lexer would reduce document changes caused by Also TCL lexer has strange Lines 205 to 210 in 16f8011
|
Its tracking a boolean comment 'level' as bit mask 0b1 separably from fold level bit mask 0b111111111110. |
Yes, I just see this. it only helps languages with top level structure (e.g. C functions). Small change to fix potential truly negative number for
diff --git a/lexers/LexRuby.cxx b/lexers/LexRuby.cxx
index 8ac792d1..cf08e221 100644
--- a/lexers/LexRuby.cxx
+++ b/lexers/LexRuby.cxx
@@ -1985,12 +1985,12 @@ void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordL
}
}
if (atEOL || (i == endPos - 1)) {
- int lev = levelPrev;
+ int lev = levelPrev + SC_FOLDLEVELBASE;
if (visibleChars == 0 && foldCompact)
lev |= SC_FOLDLEVELWHITEFLAG;
if ((levelCurrent > levelPrev) && (visibleChars > 0))
lev |= SC_FOLDLEVELHEADERFLAG;
- styler.SetLevel(lineCurrent, lev|SC_FOLDLEVELBASE);
+ styler.SetLevel(lineCurrent, lev);
lineCurrent++;
levelPrev = levelCurrent;
visibleChars = 0;
diff --git a/lexers/LexTCL.cxx b/lexers/LexTCL.cxx
index 5e34aea3..da64df57 100644
--- a/lexers/LexTCL.cxx
+++ b/lexers/LexTCL.cxx
@@ -315,7 +315,9 @@ next:
case '}':
sc.SetState(SCE_TCL_OPERATOR);
expected = true;
- --currentLevel;
+ if (currentLevel > 0) {
+ --currentLevel;
+ }
break;
case '[':
expected = true; |
full remove power-of-two assumption for @@ -1841,9 +1841,10 @@ void FoldRbDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordL
const Sci_PositionU endPos = startPos + length;
int visibleChars = 0;
Sci_Position lineCurrent = styler.GetLine(startPos);
- int levelPrev = startPos == 0 ? 0 : (styler.LevelAt(lineCurrent)
- & SC_FOLDLEVELNUMBERMASK
- & ~SC_FOLDLEVELBASE);
+ int levelPrev = 0;
+ if (startPos > 0) {
+ levelPrev = (styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
+ }
int levelCurrent = levelPrev;
char chPrev = '\0';
char chNext = styler[startPos]; |
A Scintilla patch to show unbalanced folds. The patch hard-codes the appearance but a complete implementation would allow choosing different markers. diff -r 20064c7d69d1 src/MarginView.cxx
--- a/src/MarginView.cxx Thu Jan 18 12:41:55 2024 +1100
+++ b/src/MarginView.cxx Mon Jan 22 09:51:33 2024 +1100
@@ -323,6 +323,7 @@
bool headWithTail = false;
bool isExpanded = false;
+ bool isUnbalanced = false;
if (marginStyle.ShowsFolding()) {
// Decide which fold indicator should be displayed
@@ -359,6 +360,7 @@
needWhiteClosure = LevelIsWhitespace(levelNext);
}
}
+ isUnbalanced = (levelNum > levelNextNum) && (levelNextNum < FoldLevel::Base);
}
const PRectangle rcMarker(
@@ -456,6 +458,15 @@
}
}
+ if (isUnbalanced) {
+ const XYPOSITION widthBrace = surface->WidthText(vs.styles[StyleBraceBad].font.get(), "}");
+ surface->AlphaRectangle(rcMarker, 3.0, FillStroke(ColourRGBA(0xFF, 0x00, 0x00, 0x20), ColourRGBA(0xFF, 0x00, 0x00, 0xFF)));
+ PRectangle rcBrace = rcMarker;
+ rcBrace.Move(rcMarker.Width()/2.0 - widthBrace/2.0, 0);
+ surface->DrawTextTransparent(rcBrace, vs.styles[StyleBraceBad].font.get(),
+ rcBrace.top + vs.maxAscent-1, "}", ColourRGBA(0xFF000000));
+ }
+
visibleLine++;
yposScreen += vs.lineHeight;
} |
A new flag (e.g. isUnbalanced = FlagSet(leve, FoldLevel::Negative) || ((levelNum > levelNextNum) && (levelNextNum < FoldLevel::Base)); |
Reporting the negative levels (or deepening of negative level) through a flag would help both the margin view code and applications but I do not think this should be coded into lexers. It's a generic feature that can be implemented in Scintilla. It was a mistake that |
Sorry, I still not get how your delta approach works. For Notepad2, as most lexers are written by myself, change lexer is the quickest fix. |
See zufuliu/notepad4#745, when braces or (code folding word pairs) become unbalanced, code folding may totally messed up after the line, which looks ugly.
to fix this (as some kind of error recover), most lexer requires only a single line change before setting folding level (e.g. after
atEOL
):levelNext = std::max(levelNext, SC_FOLDLEVELBASE); // or levelCurrent = std::max(levelCurrent, SC_FOLDLEVELBASE);
The text was updated successfully, but these errors were encountered: