From 41140d3c49eb232d02a52f208bc8979c0d1a21b3 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 14 Sep 2023 16:29:08 +0200 Subject: [PATCH 01/10] Add GetSubTextFromRange to ISourceText --- src/Compiler/Facilities/prim-lexing.fs | 20 +++++++++++++++ src/Compiler/Facilities/prim-lexing.fsi | 3 +++ ...ervice.SurfaceArea.netstandard20.debug.bsl | 1 + ...vice.SurfaceArea.netstandard20.release.bsl | 1 + .../FSharp.Compiler.Service.Tests.fsproj | 1 + .../SourceTextTests.fs | 25 +++++++++++++++++++ 6 files changed, 51 insertions(+) create mode 100644 tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 785b7fbdf6f..9d42077f65a 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -28,6 +28,8 @@ type ISourceText = abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit + abstract GetSubTextFromRange: range: range -> string + [] type StringText(str: string) = @@ -108,6 +110,24 @@ type StringText(str: string) = member _.CopyTo(sourceIndex, destination, destinationIndex, count) = str.CopyTo(sourceIndex, destination, destinationIndex, count) + member this.GetSubTextFromRange(range) = + let sourceText = this :> ISourceText + let startLine = range.StartLine - 1 + let line = sourceText.GetLineString startLine + + if range.StartLine = range.EndLine then + let length = range.EndColumn - range.StartColumn + line.Substring(range.StartColumn, length) + else + let firstLineContent = line.Substring(range.StartColumn) + let sb = System.Text.StringBuilder().AppendLine(firstLineContent) + + (sb, [ range.StartLine .. range.EndLine - 2 ]) + ||> List.fold (fun sb lineNumber -> sb.AppendLine(sourceText.GetLineString lineNumber)) + |> fun sb -> + let lastLine = sourceText.GetLineString(range.EndLine - 1) + sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() + module SourceText = let ofString str = StringText(str) :> ISourceText diff --git a/src/Compiler/Facilities/prim-lexing.fsi b/src/Compiler/Facilities/prim-lexing.fsi index a7c919991d3..647f8385de4 100644 --- a/src/Compiler/Facilities/prim-lexing.fsi +++ b/src/Compiler/Facilities/prim-lexing.fsi @@ -35,6 +35,9 @@ type ISourceText = /// Copies a section of the input to the given destination ad the given index abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit + /// Gets a section of the input based on a given range. + abstract GetSubTextFromRange: range: range -> string + /// Functions related to ISourceText objects module SourceText = diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 62e28427927..e5d58f2ed14 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -10179,6 +10179,7 @@ FSharp.Compiler.Text.ISourceText: Int32 GetLineCount() FSharp.Compiler.Text.ISourceText: Int32 Length FSharp.Compiler.Text.ISourceText: Int32 get_Length() FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32) +FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition() FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 62e28427927..e5d58f2ed14 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -10179,6 +10179,7 @@ FSharp.Compiler.Text.ISourceText: Int32 GetLineCount() FSharp.Compiler.Text.ISourceText: Int32 Length FSharp.Compiler.Text.ISourceText: Int32 get_Length() FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32) +FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition() FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index b761ced68fd..ad4c29b02e5 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -89,6 +89,7 @@ RangeTests.fs + Program.fs diff --git a/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs b/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs new file mode 100644 index 00000000000..25debdffe29 --- /dev/null +++ b/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs @@ -0,0 +1,25 @@ +module FSharp.Compiler.Service.Tests.SourceTextTests + +open FSharp.Compiler.Text +open NUnit.Framework + +[] +let ``Select text from a single line via the range`` () = + let sourceText = SourceText.ofString """ +let a = 2 +""" + let m = Range.mkRange "Sample.fs" (Position.mkPos 2 4) (Position.mkPos 2 5) + let v = sourceText.GetSubTextFromRange m + Assert.AreEqual("a", v) + +[] +let ``Select text from multiple lines via the range`` () = + let sourceText = SourceText.ofString """ +let a b c = + // comment + 2 +""" + let m = Range.mkRange "Sample.fs" (Position.mkPos 2 4) (Position.mkPos 4 5) + let v = sourceText.GetSubTextFromRange m + let sanitized = v.Replace("\r", "") + Assert.AreEqual("a b c =\n // comment\n 2", sanitized) From 5af4329af4e7ea7854d8c6744b4bac707208b514 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 15 Sep 2023 08:20:04 +0200 Subject: [PATCH 02/10] Add unit test for inconsistent line endings. --- tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs b/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs index 25debdffe29..f66f88915a9 100644 --- a/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs @@ -23,3 +23,11 @@ let a b c = let v = sourceText.GetSubTextFromRange m let sanitized = v.Replace("\r", "") Assert.AreEqual("a b c =\n // comment\n 2", sanitized) + +[] +let ``Inconsistent return carriage return correct text`` () = + let sourceText = SourceText.ofString "let a =\r\n // foo\n 43" + let m = Range.mkRange "Sample.fs" (Position.mkPos 1 4) (Position.mkPos 3 6) + let v = sourceText.GetSubTextFromRange m + let sanitized = v.Replace("\r", "") + Assert.AreEqual("a =\n // foo\n 43", sanitized) From 3fc6743f8852bf291c2e5001adb562f783c46bde Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 15 Sep 2023 08:23:38 +0200 Subject: [PATCH 03/10] Add capacity to StringBuilder. --- src/Compiler/Facilities/prim-lexing.fs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 9d42077f65a..5c8ed6e75c4 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -120,7 +120,12 @@ type StringText(str: string) = line.Substring(range.StartColumn, length) else let firstLineContent = line.Substring(range.StartColumn) - let sb = System.Text.StringBuilder().AppendLine(firstLineContent) + + let capacity = + [ (range.StartLine - 1) .. (range.EndLine - 1) ] + |> List.sumBy (fun lineIdx -> (sourceText.GetLineString lineIdx).Length) + + let sb = System.Text.StringBuilder(capacity).AppendLine(firstLineContent) (sb, [ range.StartLine .. range.EndLine - 2 ]) ||> List.fold (fun sb lineNumber -> sb.AppendLine(sourceText.GetLineString lineNumber)) From cf687147fbd655cdd49b1016f50999d8e927d3d8 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 15 Sep 2023 08:54:02 +0200 Subject: [PATCH 04/10] Use a for loop --- src/Compiler/Facilities/prim-lexing.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 5c8ed6e75c4..7c748ca8a7c 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -127,11 +127,11 @@ type StringText(str: string) = let sb = System.Text.StringBuilder(capacity).AppendLine(firstLineContent) - (sb, [ range.StartLine .. range.EndLine - 2 ]) - ||> List.fold (fun sb lineNumber -> sb.AppendLine(sourceText.GetLineString lineNumber)) - |> fun sb -> - let lastLine = sourceText.GetLineString(range.EndLine - 1) - sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() + for lineNumber in [ range.StartLine .. range.EndLine - 2 ] do + sb.AppendLine(sourceText.GetLineString lineNumber) |> ignore + + let lastLine = sourceText.GetLineString(range.EndLine - 1) + sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() module SourceText = From 048524ccffe1421a404f0af0ac5540f9a7353bc1 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 15 Sep 2023 09:09:22 +0200 Subject: [PATCH 05/10] Cover zero range and invalid range --- src/Compiler/Facilities/prim-lexing.fs | 50 ++++++++++++------- .../SourceTextTests.fs | 16 +++++- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 7c748ca8a7c..7b5e2da8961 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -6,7 +6,6 @@ namespace FSharp.Compiler.Text open System open System.IO -open FSharp.Compiler type ISourceText = @@ -111,27 +110,44 @@ type StringText(str: string) = str.CopyTo(sourceIndex, destination, destinationIndex, count) member this.GetSubTextFromRange(range) = - let sourceText = this :> ISourceText - let startLine = range.StartLine - 1 - let line = sourceText.GetLineString startLine - - if range.StartLine = range.EndLine then - let length = range.EndColumn - range.StartColumn - line.Substring(range.StartColumn, length) + let totalAmountOfLines = getLines.Value.Length + + if + range.StartLine = 0 + && range.StartColumn = 0 + && range.EndLine = 0 + && range.EndColumn = 0 + then + String.Empty + elif + range.StartLine < 1 + || (range.StartLine - 1) > totalAmountOfLines + || range.EndLine < 1 + || (range.EndLine - 1) > totalAmountOfLines + then + invalidArg (nameof range) "The range is outside the file boundaries" else - let firstLineContent = line.Substring(range.StartColumn) + let sourceText = this :> ISourceText + let startLine = range.StartLine - 1 + let line = sourceText.GetLineString startLine + + if range.StartLine = range.EndLine then + let length = range.EndColumn - range.StartColumn + line.Substring(range.StartColumn, length) + else + let firstLineContent = line.Substring(range.StartColumn) - let capacity = - [ (range.StartLine - 1) .. (range.EndLine - 1) ] - |> List.sumBy (fun lineIdx -> (sourceText.GetLineString lineIdx).Length) + let capacity = + [ (range.StartLine - 1) .. (range.EndLine - 1) ] + |> List.sumBy (fun lineIdx -> (sourceText.GetLineString lineIdx).Length) - let sb = System.Text.StringBuilder(capacity).AppendLine(firstLineContent) + let sb = System.Text.StringBuilder(capacity).AppendLine(firstLineContent) - for lineNumber in [ range.StartLine .. range.EndLine - 2 ] do - sb.AppendLine(sourceText.GetLineString lineNumber) |> ignore + for lineNumber in [ range.StartLine .. range.EndLine - 2 ] do + sb.AppendLine(sourceText.GetLineString lineNumber) |> ignore - let lastLine = sourceText.GetLineString(range.EndLine - 1) - sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() + let lastLine = sourceText.GetLineString(range.EndLine - 1) + sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() module SourceText = diff --git a/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs b/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs index f66f88915a9..0190c2f467d 100644 --- a/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs @@ -1,5 +1,6 @@ module FSharp.Compiler.Service.Tests.SourceTextTests +open System open FSharp.Compiler.Text open NUnit.Framework @@ -27,7 +28,20 @@ let a b c = [] let ``Inconsistent return carriage return correct text`` () = let sourceText = SourceText.ofString "let a =\r\n // foo\n 43" - let m = Range.mkRange "Sample.fs" (Position.mkPos 1 4) (Position.mkPos 3 6) + let m = Range.mkRange "Sample.fs" (Position.mkPos 1 4) (Position.mkPos 3 6) let v = sourceText.GetSubTextFromRange m let sanitized = v.Replace("\r", "") Assert.AreEqual("a =\n // foo\n 43", sanitized) + +[] +let ``Zero range should return empty string`` () = + let sourceText = SourceText.ofString "a" + let v = sourceText.GetSubTextFromRange Range.Zero + Assert.AreEqual(String.Empty, v) + +[] +let ``Invalid range should throw argument exception`` () = + let sourceText = SourceText.ofString "a" + let mInvalid = Range.mkRange "Sample.fs" (Position.mkPos 3 6) (Position.mkPos 1 4) + Assert.Throws(fun () -> sourceText.GetSubTextFromRange mInvalid |> ignore) + |> ignore From ab8ae4c92254b5f948936ca609c288de5f42ef54 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 15 Sep 2023 09:10:54 +0200 Subject: [PATCH 06/10] Add exception to xml doc. --- src/Compiler/Facilities/prim-lexing.fsi | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compiler/Facilities/prim-lexing.fsi b/src/Compiler/Facilities/prim-lexing.fsi index 647f8385de4..6e5f6da4f25 100644 --- a/src/Compiler/Facilities/prim-lexing.fsi +++ b/src/Compiler/Facilities/prim-lexing.fsi @@ -36,6 +36,7 @@ type ISourceText = abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit /// Gets a section of the input based on a given range. + /// Throws an exception when the input range is outside the file boundaries. abstract GetSubTextFromRange: range: range -> string /// Functions related to ISourceText objects From 9ba16da95885a5a74e45b5afde663a57c40a3173 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 18 Sep 2023 07:56:17 +0200 Subject: [PATCH 07/10] Add GetSubTextFromRange to test implementation. --- .../FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs index 49d46568ff4..11858e18bc9 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs @@ -64,6 +64,8 @@ module internal SourceText = member __.CopyTo(sourceIndex, destination, destinationIndex, count) = sourceText.CopyTo(sourceIndex, destination, destinationIndex, count) + + member __.GetSubTextFromRange _ = failwith "Not implemented" } sourceText From 1b8e30606e6219a9bdce85c8cf6c99f35ef5385f Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 18 Sep 2023 08:58:03 +0200 Subject: [PATCH 08/10] Add dummy GetSubTextFromRange for FSharp.Editor. --- vsintegration/src/FSharp.Editor/Common/Extensions.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index d05b5166a08..b1e78b2020f 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -163,6 +163,8 @@ module private SourceText = member _.CopyTo(sourceIndex, destination, destinationIndex, count) = sourceText.CopyTo(sourceIndex, destination, destinationIndex, count) + + member _.GetSubTextFromRange(range) = failwith "Not implemented" } sourceText From 11b1934dfce29575b162df0e4af74f7737c55ef5 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 19 Sep 2023 07:50:32 +0200 Subject: [PATCH 09/10] Remove capacity and list. --- src/Compiler/Facilities/prim-lexing.fs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 7b5e2da8961..0e073dc80ef 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -136,14 +136,9 @@ type StringText(str: string) = line.Substring(range.StartColumn, length) else let firstLineContent = line.Substring(range.StartColumn) + let sb = System.Text.StringBuilder().AppendLine(firstLineContent) - let capacity = - [ (range.StartLine - 1) .. (range.EndLine - 1) ] - |> List.sumBy (fun lineIdx -> (sourceText.GetLineString lineIdx).Length) - - let sb = System.Text.StringBuilder(capacity).AppendLine(firstLineContent) - - for lineNumber in [ range.StartLine .. range.EndLine - 2 ] do + for lineNumber in range.StartLine .. range.EndLine - 2 do sb.AppendLine(sourceText.GetLineString lineNumber) |> ignore let lastLine = sourceText.GetLineString(range.EndLine - 1) From 7afe1113113c1e381cb76ef402ab3adaaad7cc28 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 19 Sep 2023 07:51:06 +0200 Subject: [PATCH 10/10] Add additional implementations. --- .../CompilerServiceBenchmarks/SourceText.fs | 34 ++++++++++++++++++- .../src/FSharp.Editor/Common/Extensions.fs | 34 ++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs index 11858e18bc9..89d911d9b71 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs @@ -65,7 +65,39 @@ module internal SourceText = member __.CopyTo(sourceIndex, destination, destinationIndex, count) = sourceText.CopyTo(sourceIndex, destination, destinationIndex, count) - member __.GetSubTextFromRange _ = failwith "Not implemented" + member this.GetSubTextFromRange range = + let totalAmountOfLines = sourceText.Lines.Count + + if + range.StartLine = 0 + && range.StartColumn = 0 + && range.EndLine = 0 + && range.EndColumn = 0 + then + String.Empty + elif + range.StartLine < 1 + || (range.StartLine - 1) > totalAmountOfLines + || range.EndLine < 1 + || (range.EndLine - 1) > totalAmountOfLines + then + invalidArg (nameof range) "The range is outside the file boundaries" + else + let startLine = range.StartLine - 1 + let line = this.GetLineString startLine + + if range.StartLine = range.EndLine then + let length = range.EndColumn - range.StartColumn + line.Substring(range.StartColumn, length) + else + let firstLineContent = line.Substring(range.StartColumn) + let sb = System.Text.StringBuilder().AppendLine(firstLineContent) + + for lineNumber in range.StartLine .. range.EndLine - 2 do + sb.AppendLine(this.GetLineString lineNumber) |> ignore + + let lastLine = this.GetLineString(range.EndLine - 1) + sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() } sourceText diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index b1e78b2020f..8b432dfdf7f 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -164,7 +164,39 @@ module private SourceText = member _.CopyTo(sourceIndex, destination, destinationIndex, count) = sourceText.CopyTo(sourceIndex, destination, destinationIndex, count) - member _.GetSubTextFromRange(range) = failwith "Not implemented" + member this.GetSubTextFromRange range = + let totalAmountOfLines = sourceText.Lines.Count + + if + range.StartLine = 0 + && range.StartColumn = 0 + && range.EndLine = 0 + && range.EndColumn = 0 + then + String.Empty + elif + range.StartLine < 1 + || (range.StartLine - 1) > totalAmountOfLines + || range.EndLine < 1 + || (range.EndLine - 1) > totalAmountOfLines + then + invalidArg (nameof range) "The range is outside the file boundaries" + else + let startLine = range.StartLine - 1 + let line = this.GetLineString startLine + + if range.StartLine = range.EndLine then + let length = range.EndColumn - range.StartColumn + line.Substring(range.StartColumn, length) + else + let firstLineContent = line.Substring(range.StartColumn) + let sb = System.Text.StringBuilder().AppendLine(firstLineContent) + + for lineNumber in range.StartLine .. range.EndLine - 2 do + sb.AppendLine(this.GetLineString lineNumber) |> ignore + + let lastLine = this.GetLineString(range.EndLine - 1) + sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() } sourceText