From ae01ca395c371b163bd6f133e2ce5a3128a17577 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 3 Jun 2022 13:30:11 +0200 Subject: [PATCH] Consider selection range when collecting trivia. --- src/Fantomas.Core.Tests/FormatAstTests.fs | 2 +- .../FormattingSelectionOnlyTests.fs | 4 +- src/Fantomas.Core.Tests/SynLongIdentTests.fs | 1 - src/Fantomas.Core.Tests/TestHelpers.fs | 9 ++--- src/Fantomas.Core.Tests/TriviaTests.fs | 4 +- src/Fantomas.Core/CodeFormatter.fs | 13 ++---- src/Fantomas.Core/CodeFormatter.fsi | 3 +- src/Fantomas.Core/CodeFormatterImpl.fs | 15 ++++--- src/Fantomas.Core/Context.fs | 4 +- src/Fantomas.Core/Selection.fs | 2 +- src/Fantomas.Core/Trivia.fs | 40 +++++++++++++++---- src/Fantomas.Core/TriviaHelpers.fs | 4 -- 12 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/Fantomas.Core.Tests/FormatAstTests.fs b/src/Fantomas.Core.Tests/FormatAstTests.fs index af53787051..6fcbd3960e 100644 --- a/src/Fantomas.Core.Tests/FormatAstTests.fs +++ b/src/Fantomas.Core.Tests/FormatAstTests.fs @@ -13,7 +13,7 @@ let parseAndFormat fn sourceCode = |> fst let formattedCode = - CodeFormatter.FormatASTAsync(ast, [], fn sourceCode, config) + CodeFormatter.FormatASTAsync(ast, fn sourceCode, config) |> Async.RunSynchronously |> String.normalizeNewLine |> fun s -> s.TrimEnd('\n') diff --git a/src/Fantomas.Core.Tests/FormattingSelectionOnlyTests.fs b/src/Fantomas.Core.Tests/FormattingSelectionOnlyTests.fs index a42ad7e2fe..2fc25ce81f 100644 --- a/src/Fantomas.Core.Tests/FormattingSelectionOnlyTests.fs +++ b/src/Fantomas.Core.Tests/FormattingSelectionOnlyTests.fs @@ -111,9 +111,9 @@ functionApplication let ``only format trivia inside the selection`` () = formatSelectionOnly false - (mkSelection (5, 0) (6, 20)) + (mkSelection (5, 0) (6, 10)) """ - +let x = 0 diff --git a/src/Fantomas.Core.Tests/SynLongIdentTests.fs b/src/Fantomas.Core.Tests/SynLongIdentTests.fs index 962f00f09a..1cbc135087 100644 --- a/src/Fantomas.Core.Tests/SynLongIdentTests.fs +++ b/src/Fantomas.Core.Tests/SynLongIdentTests.fs @@ -397,7 +397,6 @@ let ``backticks can be added from AST only scenarios`` () = CodeFormatter.FormatASTAsync( tree, - [], None, { config with StrictMode = true diff --git a/src/Fantomas.Core.Tests/TestHelpers.fs b/src/Fantomas.Core.Tests/TestHelpers.fs index 9f462e3b26..6efde500c0 100644 --- a/src/Fantomas.Core.Tests/TestHelpers.fs +++ b/src/Fantomas.Core.Tests/TestHelpers.fs @@ -7,8 +7,6 @@ open NUnit.Framework open FsCheck open FsUnit open FSharp.Compiler.Text -open FSharp.Compiler.Syntax -open FSharp.Compiler.Xml open Fantomas.Core.FormatConfig open Fantomas.Core @@ -45,11 +43,12 @@ let formatSourceStringWithDefines defines (s: string) config = let source = CodeFormatterImpl.getSourceText s let! asts = CodeFormatterImpl.parse false source - let ast, defineCombination = + let ast = Array.filter (fun (_, d: DefineCombination) -> List.sort d = List.sort defines) asts |> Array.head + |> fst - return CodeFormatterImpl.formatWith (Some source) defineCombination ast config + return CodeFormatterImpl.formatWith (Some source) ast config None } |> Async.RunSynchronously @@ -67,7 +66,7 @@ let isValidFSharpCode isFsiFile s = |> Async.RunSynchronously let formatAST a s c = - CodeFormatter.FormatASTAsync(a, [], s, c) + CodeFormatter.FormatASTAsync(a, s, c) |> Async.RunSynchronously let equal x = diff --git a/src/Fantomas.Core.Tests/TriviaTests.fs b/src/Fantomas.Core.Tests/TriviaTests.fs index 72c437cf7a..2066ed3cb8 100644 --- a/src/Fantomas.Core.Tests/TriviaTests.fs +++ b/src/Fantomas.Core.Tests/TriviaTests.fs @@ -10,7 +10,7 @@ open Fantomas.Core.FormatConfig let private toTrivia source = let sourceText = CodeFormatterImpl.getSourceText source let ast, _ = Fantomas.FCS.Parse.parseFile false sourceText [] - Trivia.collectTrivia FormatConfig.Default sourceText ast + Trivia.collectTrivia FormatConfig.Default sourceText ast None let private toTriviaWithDefines source = let sourceText = CodeFormatterImpl.getSourceText source @@ -24,7 +24,7 @@ let private toTriviaWithDefines source = let defineCombinations = Defines.getDefineCombination hashDirectives defineCombinations - |> List.map (fun dc -> dc, Trivia.collectTrivia FormatConfig.Default sourceText ast) + |> List.map (fun dc -> dc, Trivia.collectTrivia FormatConfig.Default sourceText ast None) |> Map.ofList [] diff --git a/src/Fantomas.Core/CodeFormatter.fs b/src/Fantomas.Core/CodeFormatter.fs index 0ab9c1d5e5..2d31e85d12 100644 --- a/src/Fantomas.Core/CodeFormatter.fs +++ b/src/Fantomas.Core/CodeFormatter.fs @@ -5,18 +5,13 @@ open FSharp.Compiler.Syntax [] type CodeFormatter = static member ParseAsync(isSignature, source) : Async<(ParsedInput * string list) array> = - async { - let! asts = - CodeFormatterImpl.getSourceText source - |> CodeFormatterImpl.parse isSignature - - return asts - } + CodeFormatterImpl.getSourceText source + |> CodeFormatterImpl.parse isSignature - static member FormatASTAsync(ast: ParsedInput, defines: string list, source, config) : Async = + static member FormatASTAsync(ast: ParsedInput, source, config) : Async = let sourceAndTokens = Option.map CodeFormatterImpl.getSourceText source - CodeFormatterImpl.formatAST ast defines sourceAndTokens config + CodeFormatterImpl.formatAST ast sourceAndTokens config None |> async.Return static member FormatDocumentAsync(isSignature, source, config) = diff --git a/src/Fantomas.Core/CodeFormatter.fsi b/src/Fantomas.Core/CodeFormatter.fsi index 63a0b20665..b3a4577437 100644 --- a/src/Fantomas.Core/CodeFormatter.fsi +++ b/src/Fantomas.Core/CodeFormatter.fsi @@ -10,8 +10,7 @@ type CodeFormatter = static member ParseAsync: isSignature: bool * source: string -> Async<(ParsedInput * string list) array> /// Format an abstract syntax tree using an optional source for trivia processing - static member FormatASTAsync: - ast: ParsedInput * defines: string list * source: string option * config: FormatConfig -> Async + static member FormatASTAsync: ast: ParsedInput * source: string option * config: FormatConfig -> Async /// Format a source string using given config static member FormatDocumentAsync: isSignature: bool * source: string * config: FormatConfig -> Async diff --git a/src/Fantomas.Core/CodeFormatterImpl.fs b/src/Fantomas.Core/CodeFormatterImpl.fs index efc838ad5e..29966e0698 100644 --- a/src/Fantomas.Core/CodeFormatterImpl.fs +++ b/src/Fantomas.Core/CodeFormatterImpl.fs @@ -54,9 +54,14 @@ let parse (isSignature: bool) (source: ISourceText) : Async<(ParsedInput * Defin }) |> Async.Parallel -let formatWith (sourceText: ISourceText option) (defineCombination: DefineCombination) ast config = +let formatWith + (sourceText: ISourceText option) + (ast: ParsedInput) + (config: FormatConfig) + (selection: range option) + : string = let formattedSourceCode = - let context = Context.Context.Create config sourceText defineCombination ast + let context = Context.Context.Create config sourceText ast selection context |> genParsedInput ASTContext.Default ast @@ -72,7 +77,7 @@ let format (config: FormatConfig) (isSignature: bool) (source: ISourceText) : As asts |> Array.map (fun (ast', defineCombination) -> async { - let formattedCode = formatWith (Some source) defineCombination ast' config + let formattedCode = formatWith (Some source) ast' config None return (defineCombination, formattedCode) }) |> Async.Parallel @@ -117,5 +122,5 @@ Please raise an issue at https://fsprojects.github.io/fantomas-tools/#/fantomas/ let formatDocument config isSignature source = format config isSignature source /// Format an abstract syntax tree using given config -let formatAST ast defines sourceText config = - formatWith sourceText defines ast config +let formatAST ast sourceText config selection = + formatWith sourceText ast config selection diff --git a/src/Fantomas.Core/Context.fs b/src/Fantomas.Core/Context.fs index 745c978de2..f54ac531d9 100644 --- a/src/Fantomas.Core/Context.fs +++ b/src/Fantomas.Core/Context.fs @@ -199,10 +199,10 @@ type internal Context = FileName = String.Empty SourceText = None } - static member Create config (source: ISourceText option) (defineCombination: DefineCombination) (ast: ParsedInput) = + static member Create config (source: ISourceText option) (ast: ParsedInput) (selection: range option) = let trivia, sourceText = match source with - | Some source when not config.StrictMode -> Trivia.collectTrivia config source ast, Some source + | Some source when not config.StrictMode -> Trivia.collectTrivia config source ast selection, Some source | _ -> [], None let triviaByNodes = diff --git a/src/Fantomas.Core/Selection.fs b/src/Fantomas.Core/Selection.fs index a1776757b9..0acafa979d 100644 --- a/src/Fantomas.Core/Selection.fs +++ b/src/Fantomas.Core/Selection.fs @@ -150,7 +150,7 @@ let formatSelection MaxLineLength = maxLineLength } let selection = - CodeFormatterImpl.formatAST tree [] (Some sourceText) selectionConfig + CodeFormatterImpl.formatAST tree (Some sourceText) selectionConfig (Some selection) return Some(selection.TrimEnd([| '\r'; '\n' |])) } diff --git a/src/Fantomas.Core/Trivia.fs b/src/Fantomas.Core/Trivia.fs index af43edb058..251fb9731b 100644 --- a/src/Fantomas.Core/Trivia.fs +++ b/src/Fantomas.Core/Trivia.fs @@ -207,6 +207,7 @@ let private addTriviaToTriviaNode (startOfSourceCode: int) (triviaNodes: TriviaN let internal collectTriviaFromDirectives (source: ISourceText) (directives: ConditionalDirectiveTrivia list) + (selection: range option) : Trivia list = directives |> List.map (function @@ -216,8 +217,16 @@ let internal collectTriviaFromDirectives | ConditionalDirectiveTrivia.Else r -> { Item = Directive "#else"; Range = r } | ConditionalDirectiveTrivia.EndIf r -> { Item = Directive "#endif"; Range = r }) + |> fun trivia -> + match selection with + | None -> trivia + | Some selection -> List.filter (fun t -> RangeHelpers.rangeContainsRange selection t.Range) trivia -let internal collectTriviaFromCodeComments (source: ISourceText) (codeComments: CommentTrivia list) : Trivia list = +let internal collectTriviaFromCodeComments + (source: ISourceText) + (codeComments: CommentTrivia list) + (selection: range option) + : Trivia list = codeComments |> List.map (function | CommentTrivia.BlockComment r -> @@ -236,6 +245,10 @@ let internal collectTriviaFromCodeComments (source: ISourceText) (codeComments: ) { Item = item; Range = r }) + |> fun trivia -> + match selection with + | None -> trivia + | Some selection -> List.filter (fun t -> RangeHelpers.rangeContainsRange selection t.Range) trivia // TODO: optimize the range of which newlines are allowed in // for a selection, the newlines should be found inside the selection @@ -247,6 +260,7 @@ let internal collectTriviaFromBlankLines (source: ISourceText) (triviaNode: TriviaNodeAssigner) (codeComments: CommentTrivia list) + (codeRange: range) : Trivia list = let fileIndex = triviaNode.Range.FileIndex @@ -290,9 +304,10 @@ let internal collectTriviaFromBlankLines } ) - let max = source.GetLineCount() - 1 + let min = codeRange.StartLine - 1 + let max = codeRange.EndLine - 1 - (0, [ 0..max ]) + (min, [ min..max ]) ||> List.chooseState (fun count idx -> if ignoreLines.Contains(idx + 1) then 0, None @@ -317,7 +332,12 @@ let internal collectTriviaFromBlankLines 3. Merge trivia with triviaNodes 4. genTrivia should use ranges to identify what extra content should be added from what triviaNode *) -let collectTrivia (config: FormatConfig) (source: ISourceText) (ast: ParsedInput) : TriviaNode list = +let collectTrivia + (config: FormatConfig) + (source: ISourceText) + (ast: ParsedInput) + (selection: range option) + : TriviaNode list = let triviaNodesFromAST, directives, codeComments = match ast with | ParsedInput.ImplFile (ParsedImplFileInput (hds, mns, directives, codeComments)) -> @@ -326,9 +346,15 @@ let collectTrivia (config: FormatConfig) (source: ISourceText) (ast: ParsedInput sigAstToNode ast.FullRange mns, directives, codeComments let trivia = - [ yield! collectTriviaFromDirectives source directives - yield! collectTriviaFromCodeComments source codeComments - yield! collectTriviaFromBlankLines config source triviaNodesFromAST codeComments ] + [ yield! collectTriviaFromDirectives source directives selection + yield! collectTriviaFromCodeComments source codeComments selection + yield! + collectTriviaFromBlankLines + config + source + triviaNodesFromAST + codeComments + (Option.defaultValue ast.FullRange selection) ] |> List.sortBy (fun n -> n.Range.Start.Line, n.Range.Start.Column) // TODO: do we still need this? diff --git a/src/Fantomas.Core/TriviaHelpers.fs b/src/Fantomas.Core/TriviaHelpers.fs index 479e198095..9d97c90d2e 100644 --- a/src/Fantomas.Core/TriviaHelpers.fs +++ b/src/Fantomas.Core/TriviaHelpers.fs @@ -5,10 +5,6 @@ open Fantomas.Core.TriviaTypes [] module internal TriviaHelpers = - let findInRange (trivia: TriviaNode list) (range: Range) = - trivia - |> List.tryFind (fun t -> RangeHelpers.rangeContainsRange range t.Range) - let ``has content after after that matches`` (findTrivia: TriviaNode -> bool) (contentAfter: TriviaContent -> bool)