From de5be4a74a6de06308672aa1bd3d5713715c3e24 Mon Sep 17 00:00:00 2001 From: Phan Anh Dung Date: Sun, 5 Jan 2014 12:23:24 +0100 Subject: [PATCH] Handle multiline strings correctly Fix #107. Fix #110. --- src/Fantomas.Tests/StringTests.fs | 54 ++++++++++++++++++++++++++++++- src/Fantomas/CodeFormatter.fs | 11 ++++--- src/Fantomas/CodeFormatter.fsx | 35 +++++++++++++------- src/Fantomas/FormatConfig.fs | 4 +-- src/Fantomas/SourceParser.fs | 14 +++++--- 5 files changed, 96 insertions(+), 22 deletions(-) diff --git a/src/Fantomas.Tests/StringTests.fs b/src/Fantomas.Tests/StringTests.fs index 62a4aab6a2..a3063b7410 100644 --- a/src/Fantomas.Tests/StringTests.fs +++ b/src/Fantomas.Tests/StringTests.fs @@ -67,4 +67,56 @@ let d = 0o0777 let e = 1.40e10f let f = 23.4M let g = '\n' -""" \ No newline at end of file +""" + +[] +let ``should preserve triple-quote strings``() = + formatSourceString false " + type GetList() = + let switchvox_users_voicemail_getList_response = \"\"\" + \"\"\" + + let switchvox_users_voicemail_getList = \"\"\" + \"\"\" + + member self.X = switchvox_users_voicemail_getList_response +" config + |> prepend newline + |> should equal " +type GetList() = + let switchvox_users_voicemail_getList_response = \"\"\" + \"\"\" + let switchvox_users_voicemail_getList = \"\"\" + \"\"\" + member self.X = switchvox_users_voicemail_getList_response +" + +[] +let ``should keep triple-quote strings``() = + formatSourceString false " +[] +let main argv = + use fun1 = R.eval(R.parse(text = \"\"\" + function(i) { + x <- rnorm(1000) + y <- rnorm(1000) + m <- lm(y~x) + m$coefficients[[2]] + } + \"\"\")) + 0 +" config + |> prepend newline + |> should equal " +[] +let main argv = + use fun1 = R.eval (R.parse (text = \"\"\" + function(i) { + x <- rnorm(1000) + y <- rnorm(1000) + m <- lm(y~x) + m$coefficients[[2]] + } + \"\"\")) + 0 +" \ No newline at end of file diff --git a/src/Fantomas/CodeFormatter.fs b/src/Fantomas/CodeFormatter.fs index 2f4f46b9a5..b726465ae6 100644 --- a/src/Fantomas/CodeFormatter.fs +++ b/src/Fantomas/CodeFormatter.fs @@ -31,7 +31,13 @@ let parse isFsiFile s = let fileName = if isFsiFile then "/tmp.fsi" else "/tmp.fs" parseWith fileName s -let internal format isFsiFile s config = +let internal split (s : string) = + s.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n') + +let internal format isFsiFile (s : string) config = + // Use '\n' as the new line delimiter consistently + // It would be easier for F# parser + let s = s.Replace("\r\n", "\n").Replace("\r", "\n") let s' = Context.create config s |> genParsedInput (parse isFsiFile s) @@ -145,9 +151,6 @@ let internal getPatch startCol (lines : string []) = if m.Success && col <= startCol + 4 then RecLet else loop (i - 1) loop (lines.Length - 1) -let internal split (s : string) = - s.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n') - let internal formatRangeFromString isFsiFile startLine startCol endLine endCol (lines : _ []) (s : string) config = let s = s.Replace("\r\n", "\n").Replace("\r", "\n") // Convert from range to string positions diff --git a/src/Fantomas/CodeFormatter.fsx b/src/Fantomas/CodeFormatter.fsx index 951c1def94..6019fe147e 100644 --- a/src/Fantomas/CodeFormatter.fsx +++ b/src/Fantomas/CodeFormatter.fsx @@ -15,23 +15,36 @@ open Fantomas.CodeFormatter let config = { FormatConfig.Default with StrictMode = false } -let test s = formatSourceString false s config |> printfn "%A";; +let test (s : string) = + formatSourceString false (s.Replace("\r\n", "\n")) config |> printfn "%A";; fsi.AddPrinter (fun (p : Microsoft.FSharp.Compiler.Range.pos) -> p.ToString()) fsi.AddPrinter (fun (r : Microsoft.FSharp.Compiler.Range.range) -> r.ToString()) -let result = - formatAroundCursor false (makePos 4 10) """ -let comp = - eventually { for x in 1 .. 2 do - printfn " x = %d" x - return 3 + 4 } - """ config;; +test " +[] +let main argv = + use fun1 = R.eval(R.parse(text = \"\"\" + function(i) { + x <- rnorm(1000) + y <- rnorm(1000) + m <- lm(y~x) + m$coefficients[[2]] + } + \"\"\")) + 0 +";; -test """ -let hello() = "hello world" +test " + type GetList() = + let switchvox_users_voicemail_getList_response = \"\"\" + \"\"\" + + let switchvox_users_voicemail_getList = \"\"\" + \"\"\" -(* This is a comment. *)""";; + member self.X = switchvox_users_voicemail_getList_response +";; test """ (new CsvFile<_>(new Func<_, _, _>(fun (parent : obj) (row : string[]) -> CommonRuntime.GetNonOptionalValue("Name", CommonRuntime.ConvertString(TextConversions.AsOption(row.[0])), TextConversions.AsOption(row.[0])), CommonRuntime.GetNonOptionalValue("Distance", CommonRuntime.ConvertDecimal("", TextConversions.AsOption(row.[1])), TextConversions.AsOption(row.[1])), CommonRuntime.GetNonOptionalValue("Time", CommonRuntime.ConvertDecimal("", TextConversions.AsOption(row.[2])), TextConversions.AsOption(row.[2]))), new Func<_, _>(fun (row : _ * _ * _) -> [| CommonRuntime.ConvertStringBack(CommonRuntime.GetOptionalValue((let x, _, _ = row in x))); CommonRuntime.ConvertDecimalBack("", CommonRuntime.GetOptionalValue((let _, x, _ = row in x))); CommonRuntime.ConvertDecimalBack("", CommonRuntime.GetOptionalValue((let _, _, x = row in x))) |]), (ProviderFileSystem.readTextAtRunTimeWithDesignTimeOptions @"C:\Dev\FSharp.Data-master\src\..\tests\FSharp.Data.Tests\Data" "" "SmallTest.csv"), "", '"', true, false)).Cache() diff --git a/src/Fantomas/FormatConfig.fs b/src/Fantomas/FormatConfig.fs index f0b23b5c8d..cc025335a2 100644 --- a/src/Fantomas/FormatConfig.fs +++ b/src/Fantomas/FormatConfig.fs @@ -137,9 +137,9 @@ type internal Context = Content = ""; Positions = [||]; Comments = Dictionary(); Directives = Dictionary() } static member create config (content : string) = - let content = content.Replace("\r\n", "\n") + let content = content.Replace("\r\n", "\n").Replace("\r", "\n") let positions = - content.Split('\r', '\n') + content.Split('\n') |> Seq.map (fun s -> String.length s + 1) |> Seq.scan (+) 0 |> Seq.toArray diff --git a/src/Fantomas/SourceParser.fs b/src/Fantomas/SourceParser.fs index 583337ba70..2ea49caff6 100644 --- a/src/Fantomas/SourceParser.fs +++ b/src/Fantomas/SourceParser.fs @@ -1,6 +1,7 @@ module internal Fantomas.SourceParser open System +open System.Diagnostics open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.PrettyNaming @@ -12,6 +13,10 @@ type Composite<'a, 'b> = | Pair of 'b * 'b | Single of 'a +#if INTERACTIVE +type Debug = Console +#endif + /// Get source string content based on range value let lookup (r : range) (c : Context) = if r.EndLine <= c.Positions.Length then @@ -19,12 +24,13 @@ let lookup (r : range) (c : Context) = let finish = c.Positions.[r.EndLine-1] + r.EndColumn - 1 let content = c.Content let s = content.[start..finish] - if s.Contains("\n") then + Debug.WriteLine("Content: {0} at start = {1}, finish = {2}", s, start, finish) + if s.Contains("\\\n") then // Terrible hack to compensate the offset made by F# compiler let last = content.[c.Positions.[r.EndLine-1]..finish] - let offset = last.Length - last.TrimStart(' ').Length - if finish + offset >= content.Length then content.[start..] - else content.[start..finish + offset] + let offset = min (last.Length - last.TrimStart(' ').Length) (content.Length - finish - 1) + Debug.WriteLine("Content after patch: {0} with offset = {1}", s, offset) + content.[start..finish + offset] else s else ""