From 22e19499b97b92ae60dc97afbb8983917c87531d Mon Sep 17 00:00:00 2001 From: Robert Di Pardo Date: Fri, 16 Apr 2021 21:50:48 +1000 Subject: [PATCH] Some improvements to folding. --- lexers/LexFSharp.cxx | 59 +++++++++++++++++++++----- test/examples/fsharp/x.fs.folded | 72 ++++++++++++++++---------------- 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/lexers/LexFSharp.cxx b/lexers/LexFSharp.cxx index ec32b1ce..992449a4 100644 --- a/lexers/LexFSharp.cxx +++ b/lexers/LexFSharp.cxx @@ -546,6 +546,7 @@ void SCI_METHOD LexerFSharp::Lex(Sci_PositionU start, Sci_Position length, int i } bool LineContains(LexAccessor &styler, const char *word, const Sci_Position start, const Sci_Position end = 1); +bool MatchPPDirectiveBranch(LexAccessor &styler, const Sci_Position line); void SCI_METHOD LexerFSharp::Fold(Sci_PositionU start, Sci_Position length, int initStyle, IDocument *pAccess) { if (!options.fold) { @@ -584,17 +585,29 @@ void SCI_METHOD LexerFSharp::Fold(Sci_PositionU start, Sci_Position length, int chNext = styler.SafeGetCharAt(currentPos + 1); if (options.foldComment) { - if (options.foldCommentMultiLine && style == SCE_FSHARP_COMMENTLINE && - stylePrev != SCE_FSHARP_COMMENTLINE) { + if (options.foldCommentMultiLine && inLineComment && + (lineCurrent > 0 || styler.StyleAt(lineStartNext) == SCE_FSHARP_COMMENTLINE)) { const bool haveCommentList = LineContains(styler, "//", lineStartNext, lineNext); if (haveCommentList) { - commentLinesInFold++; - if (commentLinesInFold < 1) { + // begin fold region + if (commentLinesInFold == ZERO_LENGTH) { levelNext++; } + // start line count at 0 + commentLinesInFold++; } else { - const int8_t offset = commentLinesInFold > 0 ? commentLinesInFold : 1; - levelNext -= offset; + Sci_Position lineFold = lineCurrent; + while (lineFold > 0) { + commentLinesInFold--; + lineFold--; + if (!LineContains(styler, "//", lineFold)) { + break; + } + } + // line count should be reduced to 0, and no less + if (commentLinesInFold > ZERO_LENGTH) { + levelNext--; + } commentLinesInFold = ZERO_LENGTH; } } @@ -613,19 +626,37 @@ void SCI_METHOD LexerFSharp::Fold(Sci_PositionU start, Sci_Position length, int levelNext++; } else if (styler.Match(currentPos, "#endif")) { levelNext--; + // compensate for #else branches and import lists + Sci_Position lineFold = lineCurrent; + while (lineFold > 0) { + lineFold--; + if (MatchPPDirectiveBranch(styler, lineFold)) { + levelNext--; + break; + } + } } } if (options.foldImports && style == SCE_FSHARP_KEYWORD && LineContains(styler, "open", lineCurrent)) { const bool haveImportList = LineContains(styler, "open", lineStartNext, lineNext); if (haveImportList) { - importsInFold++; - if (importsInFold < 1) { + if (importsInFold == ZERO_LENGTH) { levelNext++; } + importsInFold++; } else { - const int8_t offset = importsInFold > 0 ? importsInFold : 1; - levelNext -= offset; + Sci_Position lineFold = lineCurrent; + while (lineFold > 0) { + importsInFold--; + lineFold--; + if (!LineContains(styler, "open", lineFold)) { + break; + } + } + if (importsInFold > ZERO_LENGTH) { + levelNext--; + } importsInFold = ZERO_LENGTH; } } @@ -672,6 +703,14 @@ bool LineContains(LexAccessor &styler, const char *word, const Sci_Position star } return found; } + +bool MatchPPDirectiveBranch(LexAccessor &styler, const Sci_Position line) { + const Sci_Position linePrev = line - 1; + const Sci_Position lineStart = styler.LineStart(line); + const Sci_Position lineStartPrev = styler.LineStart(linePrev); + return (styler.StyleAt(lineStart) == SCE_FSHARP_PREPROCESSOR && !styler.Match(lineStart, "#if")) || + (LineContains(styler, "open", lineStartPrev, linePrev) && LineContains(styler, "open", lineStart, line)); +} } // namespace LexerModule lmFSharp(SCLEX_FSHARP, LexerFSharp::LexerFactoryFSharp, lexerName, fsharpWordLists); diff --git a/test/examples/fsharp/x.fs.folded b/test/examples/fsharp/x.fs.folded index 9b68f9fc..a0188e11 100644 --- a/test/examples/fsharp/x.fs.folded +++ b/test/examples/fsharp/x.fs.folded @@ -8,39 +8,39 @@ 2 400 401 + #if DEBUG 2 401 402 + open System 0 402 402 | open System.IO - 0 402 3f8 | open System.Diagnostics - 0 3f8 3f7 #endif - 1 3f7 3f7 - 0 3f7 3f7 # 14 @"See: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/strings#remarks" - 0 3f7 3f6 // verbatim string - 0 3f6 3f3 let xmlFragment1 = @"" - 1 3f3 3f3 - 0 3f3 3f2 // triple-quoted string - 0 3f2 3ef let xmlFragment2 = """""" - 1 3ef 3ef - 2 3ef 3f0 + (* you need .NET 5.0 to compile this: - 0 3f0 3f0 https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-50#string-interpolation - 0 3f0 3ef *) - 0 3ef 3ec let interpolated = $"I think {3.0 + 0.14} is close to {System.Math.PI}!" - 1 3ec 3ec - 0 3ec 3e9 let ``a byte literal`` = '\209'B - 1 3e9 3e9 - 0 3e9 3e8 // quoted expression - 0 3e8 3e5 let expr = - 0 3e5 3e5 <@@ - 0 3e5 3e5 let foo () = "bar" - 0 3e5 3e5 foo () - 0 3e5 3e5 @@> - 1 3e5 3e5 - 0 3e5 3e2 let bigNum (unused: 'a): float option = - 0 3e2 3df Seq.init 10_000 (float >> (fun i -> i + 11.)) - 0 3df 3df |> (List.ofSeq - 0 3df 3df >> List.take 5 - 0 3df 3df >> List.fold (*) 1.0) - 0 3df 3df |> Some - 1 3df 3df - 0 3df 3d4 match bigNum () with - 0 3d4 3d4 | Some num -> sprintf "%.2f > %u" num ``a byte literal`` - 0 3d4 3d4 | None -> sprintf "%A" "Have a byte string!"B - 0 3d4 3d4 |> printfn "%s" - 1 3d4 3d4 \ No newline at end of file + 0 402 401 | open System.Diagnostics + 0 401 3ff | #endif + 1 3ff 3ff + 0 3ff 3ff # 14 @"See: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/strings#remarks" + 0 3ff 3ff // verbatim string + 0 3ff 3ff let xmlFragment1 = @"" + 1 3ff 3ff + 0 3ff 3ff // triple-quoted string + 0 3ff 3ff let xmlFragment2 = """""" + 1 3ff 3ff + 2 3ff 400 + (* you need .NET 5.0 to compile this: + 0 400 400 https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-50#string-interpolation + 0 400 3ff *) + 0 3ff 3ff let interpolated = $"I think {3.0 + 0.14} is close to {System.Math.PI}!" + 1 3ff 3ff + 0 3ff 3ff let ``a byte literal`` = '\209'B + 1 3ff 3ff + 0 3ff 3ff // quoted expression + 0 3ff 3ff let expr = + 0 3ff 3ff <@@ + 0 3ff 3ff let foo () = "bar" + 0 3ff 3ff foo () + 0 3ff 3ff @@> + 1 3ff 3ff + 0 3ff 3ff let bigNum (unused: 'a): float option = + 0 3ff 3ff Seq.init 10_000 (float >> (fun i -> i + 11.)) + 0 3ff 3ff |> (List.ofSeq + 0 3ff 3ff >> List.take 5 + 0 3ff 3ff >> List.fold (*) 1.0) + 0 3ff 3ff |> Some + 1 3ff 3ff + 0 3ff 3ff match bigNum () with + 0 3ff 3ff | Some num -> sprintf "%.2f > %u" num ``a byte literal`` + 0 3ff 3ff | None -> sprintf "%A" "Have a byte string!"B + 0 3ff 3ff |> printfn "%s" + 1 3ff 3ff \ No newline at end of file