Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 7280506

Browse files
authored
Add ISourceText to language service and use Roslyn's SourceText for FSharp.Editor (dotnet#6001)
* Initial ISourceText implementation (does not work yet) * Lexbuffer works * Removing Source. Now using only ISourceText. Added SourceText.ofString. * Fixing tests * We need to use addNewLine for tests to pass * Added test for SourceText.ofString * Trying to fix tests * Simplified ISourceText API. Added RoslynSourceTextTests * Trying to get the build working again * Re-organize prim-lexing.fsi * Handling format strings * Trying to get tests to pass * Trying to fix tests * Ignoring test * unignoring test * Fixed weak table * Removing addNewLine in sourcetext * Fixing interactive checker tests * Fixing more tests * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removed addNewLine * Removing last addNewLine. It's done * Better tests and small optimizations * Adjusting comment * Updating CompilerServiceBenchmarks * Updated nits
1 parent 04f3cc2 commit 7280506

15 files changed

+103
-35
lines changed

Commands/HelpContextService.fs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type internal FSharpHelpContextService
2525
static let userOpName = "ImplementInterfaceCodeFix"
2626
static member GetHelpTerm(checker: FSharpChecker, sourceText : SourceText, fileName, options, span: TextSpan, tokens: List<ClassifiedSpan>, textVersion, perfOptions) : Async<string option> =
2727
asyncMaybe {
28-
let! _, _, check = checker.ParseAndCheckDocument(fileName, textVersion, sourceText.ToString(), options, perfOptions, userOpName = userOpName)
28+
let! _, _, check = checker.ParseAndCheckDocument(fileName, textVersion, sourceText, options, perfOptions, userOpName = userOpName)
2929
let textLines = sourceText.Lines
3030
let lineInfo = textLines.GetLineFromPosition(span.Start)
3131
let line = lineInfo.LineNumber
@@ -34,12 +34,11 @@ type internal FSharpHelpContextService
3434
let caretColumn = textLines.GetLinePosition(span.Start).Character
3535

3636
let shouldTryToFindSurroundingIdent (token : ClassifiedSpan) =
37-
let span = token.TextSpan
38-
let content = sourceText.ToString().Substring(span.Start, span.End - span.Start)
37+
let content = sourceText.GetSubText(token.TextSpan)
3938
match token.ClassificationType with
4039
| ClassificationTypeNames.Text
4140
| ClassificationTypeNames.WhiteSpace -> true
42-
| (ClassificationTypeNames.Operator|ClassificationTypeNames.Punctuation)when content = "." -> true
41+
| (ClassificationTypeNames.Operator|ClassificationTypeNames.Punctuation)when content.Length > 0 && content.[0] = '.' -> true
4342
| _ -> false
4443

4544
let tokenInformation, col =

Commands/XmlDocCommandService.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ type internal XmlDocCommandFilter
6868
let curLineNum = wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + 1
6969
let! document = document.Value
7070
let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None)
71-
let sourceText = wpfTextView.TextBuffer.CurrentSnapshot.GetText()
71+
let! sourceText = document.GetTextAsync(CancellationToken.None)
7272
let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName)
73-
let xmlDocables = XmlDocParser.getXmlDocables (sourceText, Some parsedInput)
73+
let xmlDocables = XmlDocParser.getXmlDocables (sourceText.ToFSharpSourceText(), Some parsedInput)
7474
let xmlDocablesBelowThisLine =
7575
// +1 because looking below current line for e.g. a 'member'
7676
xmlDocables |> List.filter (fun (XmlDocable(line,_indent,_paramNames)) -> line = curLineNum+1)

Common/Extensions.fs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ module internal Microsoft.VisualStudio.FSharp.Editor.Extensions
66
open System
77
open System.IO
88
open Microsoft.CodeAnalysis
9+
open Microsoft.CodeAnalysis.Text
910
open Microsoft.CodeAnalysis.Host
11+
open Microsoft.FSharp.Compiler.Text
1012
open Microsoft.FSharp.Compiler.Ast
1113
open Microsoft.FSharp.Compiler.SourceCodeServices
1214

@@ -40,6 +42,74 @@ type Document with
4042
languageServices.GetService<'T>()
4143
|> Some
4244

45+
module private SourceText =
46+
47+
open System.Runtime.CompilerServices
48+
49+
let weakTable = ConditionalWeakTable<SourceText, ISourceText>()
50+
51+
let create (sourceText: SourceText) =
52+
let sourceText =
53+
{ new ISourceText with
54+
55+
member __.Item with get index = sourceText.[index]
56+
57+
member __.GetLineString(lineIndex) =
58+
sourceText.Lines.[lineIndex].ToString()
59+
60+
member __.GetLineCount() =
61+
sourceText.Lines.Count
62+
63+
member __.GetLastCharacterPosition() =
64+
if sourceText.Lines.Count > 0 then
65+
(sourceText.Lines.Count, sourceText.Lines.[sourceText.Lines.Count - 1].Span.Length)
66+
else
67+
(0, 0)
68+
69+
member __.GetSubTextString(start, length) =
70+
sourceText.GetSubText(TextSpan(start, length)).ToString()
71+
72+
member __.SubTextEquals(target, startIndex) =
73+
if startIndex < 0 || startIndex >= sourceText.Length then
74+
invalidArg "startIndex" "Out of range."
75+
76+
if String.IsNullOrEmpty(target) then
77+
invalidArg "target" "Is null or empty."
78+
79+
let lastIndex = startIndex + target.Length
80+
if lastIndex <= startIndex || lastIndex >= sourceText.Length then
81+
invalidArg "target" "Too big."
82+
83+
let mutable finished = false
84+
let mutable didEqual = true
85+
let mutable i = 0
86+
while not finished && i < target.Length do
87+
if target.[i] <> sourceText.[startIndex + i] then
88+
didEqual <- false
89+
finished <- true // bail out early
90+
else
91+
i <- i + 1
92+
93+
didEqual
94+
95+
member __.ContentEquals(sourceText) =
96+
match sourceText with
97+
| :? SourceText as sourceText -> sourceText.ContentEquals(sourceText)
98+
| _ -> false
99+
100+
member __.Length = sourceText.Length
101+
102+
member __.CopyTo(sourceIndex, destination, destinationIndex, count) =
103+
sourceText.CopyTo(sourceIndex, destination, destinationIndex, count)
104+
}
105+
106+
sourceText
107+
108+
type SourceText with
109+
110+
member this.ToFSharpSourceText() =
111+
SourceText.weakTable.GetValue(this, Runtime.CompilerServices.ConditionalWeakTable<_,_>.CreateValueCallback(SourceText.create))
112+
43113
type FSharpNavigationDeclarationItem with
44114
member x.RoslynGlyph : Glyph =
45115
match x.Glyph with

Completion/CompletionProvider.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ type internal FSharpCompletionProvider
105105
static member ProvideCompletionsAsyncAux(checker: FSharpChecker, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, filePath: string,
106106
textVersionHash: int, getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, languageServicePerformanceOptions: LanguageServicePerformanceOptions, intellisenseOptions: IntelliSenseOptions) =
107107
asyncMaybe {
108-
let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, languageServicePerformanceOptions, userOpName = userOpName)
108+
let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName = userOpName)
109109

110110
let textLines = sourceText.Lines
111111
let caretLinePos = textLines.GetLinePosition(caretPosition)

Completion/SignatureHelp.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type internal FSharpSignatureHelpProvider
3636

3737
// Unit-testable core routine
3838
static member internal ProvideMethodsAsyncAux(checker: FSharpChecker, documentationBuilder: IDocumentationBuilder, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, triggerIsTypedChar: char option, filePath: string, textVersionHash: int) = async {
39-
let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options, userOpName = userOpName)
39+
let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, userOpName = userOpName)
4040
match checkFileAnswer with
4141
| FSharpCheckFileAnswer.Aborted -> return None
4242
| FSharpCheckFileAnswer.Succeeded(checkFileResults) ->

Debugging/BreakpointResolutionService.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type internal FSharpBreakpointResolutionService
3737
else
3838
let textLineColumn = textLinePos.Character
3939
let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based
40-
let! parseResults = checker.ParseFile(fileName, sourceText.ToString(), parsingOptions, userOpName = userOpName)
40+
let! parseResults = checker.ParseFile(fileName, sourceText.ToFSharpSourceText(), parsingOptions, userOpName = userOpName)
4141
return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn)
4242
}
4343

Diagnostics/DocumentDiagnosticAnalyzer.fs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ type internal FSharpDocumentDiagnosticAnalyzer() =
5959

6060
static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) =
6161
async {
62-
let! parseResults = checker.ParseFile(filePath, sourceText.ToString(), parsingOptions, userOpName=userOpName)
62+
let fsSourceText = sourceText.ToFSharpSourceText()
63+
let! parseResults = checker.ParseFile(filePath, fsSourceText, parsingOptions, userOpName=userOpName)
6364
let! errors =
6465
async {
6566
match diagnosticType with
6667
| DiagnosticsType.Semantic ->
67-
let! checkResultsAnswer = checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options, userOpName=userOpName)
68+
let! checkResultsAnswer = checker.CheckFileInProject(parseResults, filePath, textVersionHash, fsSourceText, options, userOpName=userOpName)
6869
match checkResultsAnswer with
6970
| FSharpCheckFileAnswer.Aborted -> return [||]
7071
| FSharpCheckFileAnswer.Succeeded results ->

DocumentHighlights/DocumentHighlightsService.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type internal FSharpDocumentHighlightsService [<ImportingConstructor>] (checkerP
5959
let textLinePos = sourceText.Lines.GetLinePosition(position)
6060
let fcsTextLineNumber = Line.fromZ textLinePos.Line
6161
let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false)
62-
let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, languageServicePerformanceOptions, userOpName = userOpName)
62+
let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName = userOpName)
6363
let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=userOpName)
6464
let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync
6565
return

Formatting/BraceMatchingService.fs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Microsoft.VisualStudio.FSharp.Editor
44

55
open System.ComponentModel.Composition
6+
open Microsoft.CodeAnalysis.Text
67
open Microsoft.CodeAnalysis.Editor
78
open Microsoft.FSharp.Compiler.SourceCodeServices
89
open System.Runtime.InteropServices
@@ -17,9 +18,9 @@ type internal FSharpBraceMatchingService
1718

1819
static let defaultUserOpName = "BraceMatching"
1920

20-
static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, parsingOptions: FSharpParsingOptions, position: int, userOpName: string, [<Optional; DefaultParameterValue(false)>] forFormatting: bool) =
21+
static member GetBraceMatchingResult(checker: FSharpChecker, sourceText: SourceText, fileName, parsingOptions: FSharpParsingOptions, position: int, userOpName: string, [<Optional; DefaultParameterValue(false)>] forFormatting: bool) =
2122
async {
22-
let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToString(), parsingOptions, userOpName)
23+
let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToFSharpSourceText(), parsingOptions, userOpName)
2324
let isPositionInRange range =
2425
match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with
2526
| None -> false

LanguageService/FSharpCheckerExtensions.fs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,17 @@ open Microsoft.FSharp.Compiler.Ast
1010
open Microsoft.FSharp.Compiler.SourceCodeServices
1111

1212
type FSharpChecker with
13-
member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: string, userOpName: string) =
13+
member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: SourceText, userOpName: string) =
1414
asyncMaybe {
15-
let! fileParseResults = checker.ParseFile(document.FilePath, sourceText, parsingOptions, userOpName=userOpName) |> liftAsync
15+
let! fileParseResults = checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync
1616
return! fileParseResults.ParseTree
1717
}
1818

19-
member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: SourceText, userOpName: string) =
20-
checker.ParseDocument(document, parsingOptions, sourceText=sourceText.ToString(), userOpName=userOpName)
21-
22-
member checker.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) =
19+
member checker.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: SourceText, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) =
2320
async {
2421
let parseAndCheckFile =
2522
async {
26-
let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText, options, userOpName=userOpName)
23+
let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, userOpName=userOpName)
2724
return
2825
match checkFileAnswer with
2926
| FSharpCheckFileAnswer.Aborted ->
@@ -82,21 +79,21 @@ type FSharpChecker with
8279
match allowStaleResults with
8380
| Some b -> { document.FSharpOptions.LanguageServicePerformance with AllowStaleCompletionResults = b }
8481
| _ -> document.FSharpOptions.LanguageServicePerformance
85-
return! checker.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText.ToString(), options, perfOpts, userOpName=userOpName)
82+
return! checker.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText, options, perfOpts, userOpName=userOpName)
8683
}
8784

8885

89-
member checker.TryParseAndCheckFileInProject (projectOptions, fileName, source, userOpName) = async {
90-
let! (parseResults, checkAnswer) = checker.ParseAndCheckFileInProject (fileName,0, source,projectOptions, userOpName=userOpName)
86+
member checker.TryParseAndCheckFileInProject (projectOptions, fileName, sourceText: SourceText, userOpName) = async {
87+
let! (parseResults, checkAnswer) = checker.ParseAndCheckFileInProject (fileName,0,sourceText.ToFSharpSourceText(),projectOptions, userOpName=userOpName)
9188
match checkAnswer with
9289
| FSharpCheckFileAnswer.Aborted -> return None
9390
| FSharpCheckFileAnswer.Succeeded checkResults -> return Some (parseResults,checkResults)
9491
}
9592

9693

97-
member checker.GetAllUsesOfAllSymbolsInSourceString (projectOptions, fileName, source: string, checkForUnusedOpens, userOpName) = async {
94+
member checker.GetAllUsesOfAllSymbolsInSourceString (projectOptions, fileName, sourceText: SourceText, checkForUnusedOpens, userOpName) = async {
9895

99-
let! parseAndCheckResults = checker.TryParseAndCheckFileInProject (projectOptions, fileName, source, userOpName=userOpName)
96+
let! parseAndCheckResults = checker.TryParseAndCheckFileInProject (projectOptions, fileName, sourceText, userOpName=userOpName)
10097
match parseAndCheckResults with
10198
| None -> return [||]
10299
| Some(_parseResults,checkResults) ->

0 commit comments

Comments
 (0)