Skip to content

Commit 910611c

Browse files
author
Omar Tawfik
authored
Merge pull request #1428 from otawfik-ms/roslyn-debugging
Enabled debugging services in Roslyn branch
2 parents 21d2b38 + e209336 commit 910611c

14 files changed

+516
-202
lines changed

vsintegration/src/FSharp.Editor/BraceMatchingService.fs

Lines changed: 24 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,117 +3,37 @@
33
namespace Microsoft.VisualStudio.FSharp.Editor
44

55
open System
6-
open System.Composition
7-
open System.Collections.Concurrent
8-
open System.Collections.Generic
9-
open System.Threading
106
open System.Threading.Tasks
11-
open System.Linq
12-
13-
open Microsoft.CodeAnalysis
14-
open Microsoft.CodeAnalysis.Classification
157
open Microsoft.CodeAnalysis.Editor
16-
open Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching
17-
open Microsoft.CodeAnalysis.Editor.Shared.Utilities
18-
open Microsoft.CodeAnalysis.Host.Mef
19-
open Microsoft.CodeAnalysis.Text
20-
218
open Microsoft.VisualStudio.FSharp.LanguageService
22-
open Microsoft.VisualStudio.Text
23-
open Microsoft.VisualStudio.Text.Tagging
24-
25-
open Microsoft.FSharp.Compiler.Parser
269
open Microsoft.FSharp.Compiler.SourceCodeServices
2710

28-
// FSROSLYNTODO: add defines flags if available from project sites and files
29-
3011
[<ExportBraceMatcher(FSharpCommonConstants.FSharpLanguageName)>]
3112
type internal FSharpBraceMatchingService() =
32-
33-
static let supportedBraceTypes = [
34-
('(', ')');
35-
('<', '>');
36-
('[', ']');
37-
('{', '}');
38-
]
39-
40-
static let ignoredClassificationTypes = [
41-
ClassificationTypeNames.Comment;
42-
ClassificationTypeNames.StringLiteral;
43-
ClassificationTypeNames.ExcludedCode;
44-
]
45-
46-
static let getBraceMatchingResult(sourceText: SourceText, fileName: Option<string>, defines: string list, position: int, cancellationToken: CancellationToken) : Option<BraceMatchingResult> =
47-
if position < 0 || position >= sourceText.Length then
48-
None
49-
else
50-
let shouldBeIgnored(characterPosition) =
51-
let textSpan = TextSpan(characterPosition, 1)
52-
let classifiedSpans = FSharpColorizationService.GetColorizationData(sourceText, textSpan, fileName, defines, cancellationToken)
53-
if classifiedSpans.Any() then
54-
ignoredClassificationTypes |> Seq.contains (classifiedSpans.First().ClassificationType)
55-
else
56-
false
57-
58-
if shouldBeIgnored(position) then
59-
None
60-
else
61-
let currentCharacter = sourceText.[position]
62-
63-
let proceedToStartOfString(i) = i - 1
64-
let proceedToEndOfString(i) = i + 1
6513

66-
let afterEndOfString(i) = i >= sourceText.Length
67-
let beforeStartOfString(i) = i < 0
68-
69-
let pickBraceType(leftBrace, rightBrace) =
70-
if currentCharacter = leftBrace then Some(proceedToEndOfString, afterEndOfString, leftBrace, rightBrace)
71-
else if currentCharacter = rightBrace then Some(proceedToStartOfString, beforeStartOfString, rightBrace, leftBrace)
72-
else None
73-
74-
match supportedBraceTypes |> List.tryPick(pickBraceType) with
75-
| None -> None
76-
| Some(proceedFunc, stoppingCondition, matchedBrace, nonMatchedBrace) ->
77-
let mutable currentPosition = proceedFunc position
78-
let mutable result = None
79-
let mutable braceDepth = 0
80-
81-
while result.IsSome = false && stoppingCondition(currentPosition) = false do
82-
cancellationToken.ThrowIfCancellationRequested()
83-
if shouldBeIgnored(currentPosition) = false then
84-
if sourceText.[currentPosition] = matchedBrace then
85-
braceDepth <- braceDepth + 1
86-
else if sourceText.[currentPosition] = nonMatchedBrace then
87-
if braceDepth = 0 then
88-
result <- Some(BraceMatchingResult(TextSpan(min position currentPosition, 1), TextSpan(max position currentPosition, 1)))
89-
else
90-
braceDepth <- braceDepth - 1
91-
currentPosition <- proceedFunc currentPosition
92-
result
14+
static member GetBraceMatchingResult(sourceText, fileName, options, position) = async {
15+
let isPositionInRange(range) =
16+
let span = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range)
17+
span.Start <= position && position <= span.End
18+
let! matchedBraces = FSharpChecker.Instance.MatchBracesAlternate(fileName, sourceText.ToString(), options)
9319

20+
return matchedBraces |> Seq.tryFind(fun(left, right) -> isPositionInRange(left) || isPositionInRange(right))
21+
}
22+
9423
interface IBraceMatcher with
95-
member this.FindBracesAsync(document: Document, position: int, cancellationToken: CancellationToken): Task<Nullable<BraceMatchingResult>> =
96-
document.GetTextAsync(cancellationToken).ContinueWith(
97-
fun (sourceTextTask: Task<SourceText>) ->
98-
if sourceTextTask.Status = TaskStatus.RanToCompletion then
99-
try match getBraceMatchingResult(sourceTextTask.Result, Some(document.Name), [], position, cancellationToken) with
100-
| None -> Nullable()
101-
| Some(braceMatchingResult) -> Nullable(braceMatchingResult)
102-
with ex ->
103-
Assert.Exception(ex)
104-
reraise()
105-
else
106-
raise(sourceTextTask.Exception.GetBaseException())
107-
, cancellationToken)
108-
109-
// Helper function to proxy Roslyn types to tests
110-
static member FindMatchingBrace(sourceText: SourceText, fileName: Option<string>, defines: string list, position: int, cancellationToken: CancellationToken) : Option<int> =
111-
match getBraceMatchingResult(sourceText, fileName, defines, position, cancellationToken) with
112-
| None -> None
113-
| Some(braceMatchingResult) ->
114-
if braceMatchingResult.LeftSpan.Start = position then
115-
Some(braceMatchingResult.RightSpan.Start)
116-
else if braceMatchingResult.RightSpan.Start = position then
117-
Some(braceMatchingResult.LeftSpan.Start)
118-
else
119-
None
24+
member this.FindBracesAsync(document, position, cancellationToken) =
25+
let computation = async {
26+
let options = CommonRoslynHelpers.GetFSharpProjectOptionsForRoslynProject(document.Project)
27+
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
28+
let! result = FSharpBraceMatchingService.GetBraceMatchingResult(sourceText, document.Name, options, position)
29+
30+
return match result with
31+
| None -> Nullable()
32+
| Some(left, right) ->
33+
Nullable(BraceMatchingResult(
34+
CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, left),
35+
CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, right)))
36+
}
37+
38+
Async.StartAsTask(computation, TaskCreationOptions.None, cancellationToken)
39+
.ContinueWith(CommonRoslynHelpers.GetCompletedTaskResult, cancellationToken)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor
4+
5+
open System
6+
open System.Composition
7+
open System.Collections.Concurrent
8+
open System.Collections.Generic
9+
open System.Threading
10+
open System.Threading.Tasks
11+
open System.Linq
12+
13+
open Microsoft.CodeAnalysis
14+
open Microsoft.CodeAnalysis.Classification
15+
open Microsoft.CodeAnalysis.Editor
16+
open Microsoft.CodeAnalysis.Editor.Implementation.Debugging
17+
open Microsoft.CodeAnalysis.Editor.Shared.Utilities
18+
open Microsoft.CodeAnalysis.Formatting
19+
open Microsoft.CodeAnalysis.Host.Mef
20+
open Microsoft.CodeAnalysis.Text
21+
22+
open Microsoft.VisualStudio.FSharp.LanguageService
23+
open Microsoft.VisualStudio.Text
24+
open Microsoft.VisualStudio.Text.Tagging
25+
26+
open Microsoft.FSharp.Compiler.Parser
27+
open Microsoft.FSharp.Compiler.SourceCodeServices
28+
open Microsoft.FSharp.Compiler.Range
29+
30+
[<Shared>]
31+
[<ExportLanguageService(typeof<IBreakpointResolutionService>, FSharpCommonConstants.FSharpLanguageName)>]
32+
type internal FSharpBreakpointResolutionService() =
33+
34+
static member GetBreakpointLocation(sourceText: SourceText, fileName: string, textSpan: TextSpan, options: FSharpProjectOptions) = async {
35+
let! parseResults = FSharpChecker.Instance.ParseFileInProject(fileName, sourceText.ToString(), options)
36+
let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start)
37+
38+
let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based
39+
let textColumnNumber = textSpan.Start - textLine.Start
40+
41+
return parseResults.ValidateBreakpointLocation(mkPos textLineNumber textColumnNumber)
42+
}
43+
44+
interface IBreakpointResolutionService with
45+
member this.ResolveBreakpointAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken): Task<BreakpointResolutionResult> =
46+
let computation = async {
47+
let options = CommonRoslynHelpers.GetFSharpProjectOptionsForRoslynProject(document.Project)
48+
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
49+
let! location = FSharpBreakpointResolutionService.GetBreakpointLocation(sourceText, document.Name, textSpan, options)
50+
51+
return match location with
52+
| None -> null
53+
| Some(range) -> BreakpointResolutionResult.CreateSpanResult(document, CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range))
54+
}
55+
56+
Async.StartAsTask(computation, TaskCreationOptions.None, cancellationToken)
57+
.ContinueWith(CommonRoslynHelpers.GetCompletedTaskResult, cancellationToken)
58+
59+
// FSROSLYNTODO: enable placing breakpoints by when user suplies fully-qualified function names
60+
member this.ResolveBreakpointsAsync(_, _, _): Task<IEnumerable<BreakpointResolutionResult>> =
61+
Task.FromResult(Enumerable.Empty<BreakpointResolutionResult>())

0 commit comments

Comments
 (0)