Skip to content

Add GetSubTextFromRange to ISourceText #15979

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

Merged
merged 11 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion src/Compiler/Facilities/prim-lexing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ namespace FSharp.Compiler.Text

open System
open System.IO
open FSharp.Compiler

type ISourceText =

Expand All @@ -28,6 +27,8 @@ type ISourceText =

abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit

abstract GetSubTextFromRange: range: range -> string

[<Sealed>]
type StringText(str: string) =

Expand Down Expand Up @@ -108,6 +109,41 @@ type StringText(str: string) =
member _.CopyTo(sourceIndex, destination, destinationIndex, count) =
str.CopyTo(sourceIndex, destination, destinationIndex, count)

member this.GetSubTextFromRange(range) =
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 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)

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 =

let ofString str = StringText(str) :> ISourceText
Expand Down
4 changes: 4 additions & 0 deletions src/Compiler/Facilities/prim-lexing.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ 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.
/// <exception cref="System.ArgumentException">Throws an exception when the input range is outside the file boundaries.</exception>
abstract GetSubTextFromRange: range: range -> string

/// Functions related to ISourceText objects
module SourceText =

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<Link>RangeTests.fs</Link>
</Compile>
<Compile Include="TooltipTests.fs" />
<Compile Include="SourceTextTests.fs" />
<Compile Include="..\service\Program.fs">
<Link>Program.fs</Link>
</Compile>
Expand Down
47 changes: 47 additions & 0 deletions tests/FSharp.Compiler.Service.Tests/SourceTextTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module FSharp.Compiler.Service.Tests.SourceTextTests

open System
open FSharp.Compiler.Text
open NUnit.Framework

[<Test>]
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)

[<Test>]
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)

[<Test>]
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)

[<Test>]
let ``Zero range should return empty string`` () =
let sourceText = SourceText.ofString "a"
let v = sourceText.GetSubTextFromRange Range.Zero
Assert.AreEqual(String.Empty, v)

[<Test>]
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<ArgumentException>(fun () -> sourceText.GetSubTextFromRange mInvalid |> ignore)
|> ignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,40 @@ module internal SourceText =

member __.CopyTo(sourceIndex, destination, destinationIndex, count) =
sourceText.CopyTo(sourceIndex, destination, destinationIndex, count)

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
Expand Down
34 changes: 34 additions & 0 deletions vsintegration/src/FSharp.Editor/Common/Extensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,40 @@ module private SourceText =

member _.CopyTo(sourceIndex, destination, destinationIndex, count) =
sourceText.CopyTo(sourceIndex, destination, destinationIndex, count)

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
Expand Down