Skip to content

Commit d16af0e

Browse files
authored
Add GetSubTextFromRange to ISourceText (#15979)
* Add GetSubTextFromRange to ISourceText
1 parent 111c08b commit d16af0e

File tree

8 files changed

+159
-1
lines changed

8 files changed

+159
-1
lines changed

src/Compiler/Facilities/prim-lexing.fs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ namespace FSharp.Compiler.Text
66

77
open System
88
open System.IO
9-
open FSharp.Compiler
109

1110
type ISourceText =
1211

@@ -28,6 +27,8 @@ type ISourceText =
2827

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

30+
abstract GetSubTextFromRange: range: range -> string
31+
3132
[<Sealed>]
3233
type StringText(str: string) =
3334

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

112+
member this.GetSubTextFromRange(range) =
113+
let totalAmountOfLines = getLines.Value.Length
114+
115+
if
116+
range.StartLine = 0
117+
&& range.StartColumn = 0
118+
&& range.EndLine = 0
119+
&& range.EndColumn = 0
120+
then
121+
String.Empty
122+
elif
123+
range.StartLine < 1
124+
|| (range.StartLine - 1) > totalAmountOfLines
125+
|| range.EndLine < 1
126+
|| (range.EndLine - 1) > totalAmountOfLines
127+
then
128+
invalidArg (nameof range) "The range is outside the file boundaries"
129+
else
130+
let sourceText = this :> ISourceText
131+
let startLine = range.StartLine - 1
132+
let line = sourceText.GetLineString startLine
133+
134+
if range.StartLine = range.EndLine then
135+
let length = range.EndColumn - range.StartColumn
136+
line.Substring(range.StartColumn, length)
137+
else
138+
let firstLineContent = line.Substring(range.StartColumn)
139+
let sb = System.Text.StringBuilder().AppendLine(firstLineContent)
140+
141+
for lineNumber in range.StartLine .. range.EndLine - 2 do
142+
sb.AppendLine(sourceText.GetLineString lineNumber) |> ignore
143+
144+
let lastLine = sourceText.GetLineString(range.EndLine - 1)
145+
sb.Append(lastLine.Substring(0, range.EndColumn)).ToString()
146+
111147
module SourceText =
112148

113149
let ofString str = StringText(str) :> ISourceText

src/Compiler/Facilities/prim-lexing.fsi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ type ISourceText =
3535
/// Copies a section of the input to the given destination ad the given index
3636
abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit
3737

38+
/// Gets a section of the input based on a given range.
39+
/// <exception cref="System.ArgumentException">Throws an exception when the input range is outside the file boundaries.</exception>
40+
abstract GetSubTextFromRange: range: range -> string
41+
3842
/// Functions related to ISourceText objects
3943
module SourceText =
4044

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10179,6 +10179,7 @@ FSharp.Compiler.Text.ISourceText: Int32 GetLineCount()
1017910179
FSharp.Compiler.Text.ISourceText: Int32 Length
1018010180
FSharp.Compiler.Text.ISourceText: Int32 get_Length()
1018110181
FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32)
10182+
FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range)
1018210183
FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32)
1018310184
FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition()
1018410185
FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32)

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10179,6 +10179,7 @@ FSharp.Compiler.Text.ISourceText: Int32 GetLineCount()
1017910179
FSharp.Compiler.Text.ISourceText: Int32 Length
1018010180
FSharp.Compiler.Text.ISourceText: Int32 get_Length()
1018110181
FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32)
10182+
FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range)
1018210183
FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32)
1018310184
FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition()
1018410185
FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32)

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
<Link>RangeTests.fs</Link>
9595
</Compile>
9696
<Compile Include="TooltipTests.fs" />
97+
<Compile Include="SourceTextTests.fs" />
9798
<Compile Include="..\service\Program.fs">
9899
<Link>Program.fs</Link>
99100
</Compile>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module FSharp.Compiler.Service.Tests.SourceTextTests
2+
3+
open System
4+
open FSharp.Compiler.Text
5+
open NUnit.Framework
6+
7+
[<Test>]
8+
let ``Select text from a single line via the range`` () =
9+
let sourceText = SourceText.ofString """
10+
let a = 2
11+
"""
12+
let m = Range.mkRange "Sample.fs" (Position.mkPos 2 4) (Position.mkPos 2 5)
13+
let v = sourceText.GetSubTextFromRange m
14+
Assert.AreEqual("a", v)
15+
16+
[<Test>]
17+
let ``Select text from multiple lines via the range`` () =
18+
let sourceText = SourceText.ofString """
19+
let a b c =
20+
// comment
21+
2
22+
"""
23+
let m = Range.mkRange "Sample.fs" (Position.mkPos 2 4) (Position.mkPos 4 5)
24+
let v = sourceText.GetSubTextFromRange m
25+
let sanitized = v.Replace("\r", "")
26+
Assert.AreEqual("a b c =\n // comment\n 2", sanitized)
27+
28+
[<Test>]
29+
let ``Inconsistent return carriage return correct text`` () =
30+
let sourceText = SourceText.ofString "let a =\r\n // foo\n 43"
31+
let m = Range.mkRange "Sample.fs" (Position.mkPos 1 4) (Position.mkPos 3 6)
32+
let v = sourceText.GetSubTextFromRange m
33+
let sanitized = v.Replace("\r", "")
34+
Assert.AreEqual("a =\n // foo\n 43", sanitized)
35+
36+
[<Test>]
37+
let ``Zero range should return empty string`` () =
38+
let sourceText = SourceText.ofString "a"
39+
let v = sourceText.GetSubTextFromRange Range.Zero
40+
Assert.AreEqual(String.Empty, v)
41+
42+
[<Test>]
43+
let ``Invalid range should throw argument exception`` () =
44+
let sourceText = SourceText.ofString "a"
45+
let mInvalid = Range.mkRange "Sample.fs" (Position.mkPos 3 6) (Position.mkPos 1 4)
46+
Assert.Throws<ArgumentException>(fun () -> sourceText.GetSubTextFromRange mInvalid |> ignore)
47+
|> ignore

tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,40 @@ module internal SourceText =
6464

6565
member __.CopyTo(sourceIndex, destination, destinationIndex, count) =
6666
sourceText.CopyTo(sourceIndex, destination, destinationIndex, count)
67+
68+
member this.GetSubTextFromRange range =
69+
let totalAmountOfLines = sourceText.Lines.Count
70+
71+
if
72+
range.StartLine = 0
73+
&& range.StartColumn = 0
74+
&& range.EndLine = 0
75+
&& range.EndColumn = 0
76+
then
77+
String.Empty
78+
elif
79+
range.StartLine < 1
80+
|| (range.StartLine - 1) > totalAmountOfLines
81+
|| range.EndLine < 1
82+
|| (range.EndLine - 1) > totalAmountOfLines
83+
then
84+
invalidArg (nameof range) "The range is outside the file boundaries"
85+
else
86+
let startLine = range.StartLine - 1
87+
let line = this.GetLineString startLine
88+
89+
if range.StartLine = range.EndLine then
90+
let length = range.EndColumn - range.StartColumn
91+
line.Substring(range.StartColumn, length)
92+
else
93+
let firstLineContent = line.Substring(range.StartColumn)
94+
let sb = System.Text.StringBuilder().AppendLine(firstLineContent)
95+
96+
for lineNumber in range.StartLine .. range.EndLine - 2 do
97+
sb.AppendLine(this.GetLineString lineNumber) |> ignore
98+
99+
let lastLine = this.GetLineString(range.EndLine - 1)
100+
sb.Append(lastLine.Substring(0, range.EndColumn)).ToString()
67101
}
68102

69103
sourceText

vsintegration/src/FSharp.Editor/Common/Extensions.fs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,40 @@ module private SourceText =
163163

164164
member _.CopyTo(sourceIndex, destination, destinationIndex, count) =
165165
sourceText.CopyTo(sourceIndex, destination, destinationIndex, count)
166+
167+
member this.GetSubTextFromRange range =
168+
let totalAmountOfLines = sourceText.Lines.Count
169+
170+
if
171+
range.StartLine = 0
172+
&& range.StartColumn = 0
173+
&& range.EndLine = 0
174+
&& range.EndColumn = 0
175+
then
176+
String.Empty
177+
elif
178+
range.StartLine < 1
179+
|| (range.StartLine - 1) > totalAmountOfLines
180+
|| range.EndLine < 1
181+
|| (range.EndLine - 1) > totalAmountOfLines
182+
then
183+
invalidArg (nameof range) "The range is outside the file boundaries"
184+
else
185+
let startLine = range.StartLine - 1
186+
let line = this.GetLineString startLine
187+
188+
if range.StartLine = range.EndLine then
189+
let length = range.EndColumn - range.StartColumn
190+
line.Substring(range.StartColumn, length)
191+
else
192+
let firstLineContent = line.Substring(range.StartColumn)
193+
let sb = System.Text.StringBuilder().AppendLine(firstLineContent)
194+
195+
for lineNumber in range.StartLine .. range.EndLine - 2 do
196+
sb.AppendLine(this.GetLineString lineNumber) |> ignore
197+
198+
let lastLine = this.GetLineString(range.EndLine - 1)
199+
sb.Append(lastLine.Substring(0, range.EndColumn)).ToString()
166200
}
167201

168202
sourceText

0 commit comments

Comments
 (0)