Skip to content
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
1 change: 1 addition & 0 deletions src/fsharp/ErrorResolutionHints.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ let FilterPredictions (unknownIdent:string) (predictionsF:ErrorLogger.Suggestion
|> Seq.map snd
|> Seq.toList

/// Formats the given predictions according to the error style.
let FormatPredictions errorStyle normalizeF (predictions: (float * string) list) =
match predictions with
| [] -> System.String.Empty
Expand Down
31 changes: 31 additions & 0 deletions vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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.

namespace rec Microsoft.VisualStudio.FSharp.Editor

open System.Composition
open System.Collections.Immutable
open System.Threading.Tasks
open Microsoft.CodeAnalysis.CodeFixes
open Microsoft.CodeAnalysis.CodeActions

[<ExportCodeFixProvider(FSharpCommonConstants.FSharpLanguageName, Name = "ProposeUpperCaseLabel"); Shared>]
type internal FSharpProposeUpperCaseLabelCodeFixProvider
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: ProjectInfoManager
) =
inherit CodeFixProvider()
let fixableDiagnosticIds = ["FS0053"]

override __.FixableDiagnosticIds = fixableDiagnosticIds.ToImmutableArray()

override __.RegisterCodeFixesAsync context : Task =
asyncMaybe {
let textChanger (originalText: string) = originalText.[0].ToString().ToUpper() + originalText.Substring(1)
let! solutionChanger, originalText = SymbolHelpers.changeAllSymbolReferences(context.Document, context.Span, textChanger, projectInfoManager, checkerProvider.Checker)
let title = FSComp.SR.replaceWithSuggestion (textChanger originalText)
context.RegisterCodeFix(
CodeAction.Create(title, solutionChanger, title),
(context.Diagnostics |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id)).ToImmutableArray())
} |> Async.ignore |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider() =
|> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id)
|> Seq.iter (fun diagnostic ->
let message = diagnostic.GetMessage()
let splitted = message.Split([|maybeString|], StringSplitOptions.None)
if splitted.Length > 1 then
let parts = message.Split([| maybeString |], StringSplitOptions.None)
if parts.Length > 1 then
let suggestions =
splitted.[1].Split([|' '; '\r'; '\n'|], StringSplitOptions.RemoveEmptyEntries)
parts.[1].Split([|' '; '\r'; '\n'|], StringSplitOptions.RemoveEmptyEntries)
|> Array.map (fun s -> s.Trim())

let diagnostics = [| diagnostic |].ToImmutableArray()
Expand Down
44 changes: 15 additions & 29 deletions vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,20 @@ module internal Extensions =
try Path.GetFullPath path
with _ -> path

type FSharpChecker with
member this.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions) : Async<(Ast.ParsedInput * FSharpCheckFileResults) option> =
async {
let! cancellationToken = Async.CancellationToken
let! sourceText = document.GetTextAsync()
let! textVersion = document.GetTextVersionAsync(cancellationToken)
let! parseResults, checkFileAnswer = this.ParseAndCheckFileInProject(document.FilePath, textVersion.GetHashCode(), sourceText.ToString(), options)
return
match parseResults.ParseTree, checkFileAnswer with
| _, FSharpCheckFileAnswer.Aborted
| None, _ -> None
| Some parsedInput, FSharpCheckFileAnswer.Succeeded checkResults -> Some (parsedInput, checkResults)
}

type FSharpSymbol with
member this.IsInternalToProject =
match this with
Expand Down Expand Up @@ -511,32 +525,4 @@ module internal Extensions =
| GlyphMajor.Error -> Glyph.Error
| _ -> Glyph.None

type Async<'a> with
/// Creates an asynchronous workflow that runs the asynchronous workflow given as an argument at most once.
/// When the returned workflow is started for the second time, it reuses the result of the previous execution.
static member Cache (input : Async<'T>) =
let agent = MailboxProcessor<AsyncReplyChannel<_>>.Start <| fun agent ->
async {
let! replyCh = agent.Receive ()
let! res = input
replyCh.Reply res
while true do
let! replyCh = agent.Receive ()
replyCh.Reply res
}
async { return! agent.PostAndAsyncReply id }

static member inline Map (f: 'a -> 'b) (input: Async<'a>) : Async<'b> =
async {
let! result = input
return f result
}

type AsyncBuilder with
member __.Bind(computation: System.Threading.Tasks.Task<'a>, binder: 'a -> Async<'b>): Async<'b> =
async {
let! a = Async.AwaitTask computation
return! binder a
}

member __.ReturnFrom(computation: System.Threading.Tasks.Task<'a>): Async<'a> = Async.AwaitTask computation

Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,4 @@ module internal RoslynExtensions =
member this.GetDependentProjects() =
[ for project in this.Solution.Projects do
if project.ProjectReferences |> Seq.exists (fun ref -> ref.ProjectId = this.Id) then
yield project ]
yield project ]
196 changes: 195 additions & 1 deletion vsintegration/src/FSharp.Editor/Common/Pervasive.fs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[<AutoOpen>]
module Microsoft.VisualStudio.FSharp.Pervasive
module Microsoft.VisualStudio.FSharp.Editor.Pervasive

open System
open System.Diagnostics

[<RequireQualifiedAccess>]
module String =
Expand All @@ -24,3 +25,196 @@ type System.IServiceProvider with
member x.GetService<'T>() = x.GetService(typeof<'T>) :?> 'T
member x.GetService<'S, 'T>() = x.GetService(typeof<'S>) :?> 'T

[<Sealed>]
type MaybeBuilder () =
// 'T -> M<'T>
[<DebuggerStepThrough>]
member inline __.Return value: 'T option =
Some value

// M<'T> -> M<'T>
[<DebuggerStepThrough>]
member inline __.ReturnFrom value: 'T option =
value

// unit -> M<'T>
[<DebuggerStepThrough>]
member inline __.Zero (): unit option =
Some () // TODO: Should this be None?

// (unit -> M<'T>) -> M<'T>
[<DebuggerStepThrough>]
member __.Delay (f: unit -> 'T option): 'T option =
f ()

// M<'T> -> M<'T> -> M<'T>
// or
// M<unit> -> M<'T> -> M<'T>
[<DebuggerStepThrough>]
member inline __.Combine (r1, r2: 'T option): 'T option =
match r1 with
| None ->
None
| Some () ->
r2

// M<'T> * ('T -> M<'U>) -> M<'U>
[<DebuggerStepThrough>]
member inline __.Bind (value, f: 'T -> 'U option): 'U option =
Option.bind f value

// 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable
[<DebuggerStepThrough>]
member __.Using (resource: ('T :> System.IDisposable), body: _ -> _ option): _ option =
try body resource
finally
if not <| obj.ReferenceEquals (null, box resource) then
resource.Dispose ()

// (unit -> bool) * M<'T> -> M<'T>
[<DebuggerStepThrough>]
member x.While (guard, body: _ option): _ option =
if guard () then
// OPTIMIZE: This could be simplified so we don't need to make calls to Bind and While.
x.Bind (body, (fun () -> x.While (guard, body)))
else
x.Zero ()

// seq<'T> * ('T -> M<'U>) -> M<'U>
// or
// seq<'T> * ('T -> M<'U>) -> seq<M<'U>>
[<DebuggerStepThrough>]
member x.For (sequence: seq<_>, body: 'T -> unit option): _ option =
// OPTIMIZE: This could be simplified so we don't need to make calls to Using, While, Delay.
x.Using (sequence.GetEnumerator (), fun enum ->
x.While (
enum.MoveNext,
x.Delay (fun () ->
body enum.Current)))

let maybe = MaybeBuilder()

[<Sealed>]
type AsyncMaybeBuilder () =
[<DebuggerStepThrough>]
member __.Return value : Async<'T option> = Some value |> async.Return

[<DebuggerStepThrough>]
member __.ReturnFrom value : Async<'T option> = value

[<DebuggerStepThrough>]
member __.ReturnFrom (value: 'T option) : Async<'T option> = async.Return value

[<DebuggerStepThrough>]
member __.Zero () : Async<unit option> =
Some () |> async.Return

[<DebuggerStepThrough>]
member __.Delay (f : unit -> Async<'T option>) : Async<'T option> = f ()

[<DebuggerStepThrough>]
member __.Combine (r1, r2 : Async<'T option>) : Async<'T option> =
async {
let! r1' = r1
match r1' with
| None -> return None
| Some () -> return! r2
}

[<DebuggerStepThrough>]
member __.Bind (value: Async<'T option>, f : 'T -> Async<'U option>) : Async<'U option> =
async {
let! value' = value
match value' with
| None -> return None
| Some result -> return! f result
}

[<DebuggerStepThrough>]
member __.Bind (value: System.Threading.Tasks.Task<'T>, f : 'T -> Async<'U option>) : Async<'U option> =
async {
let! value' = Async.AwaitTask value
return! f value'
}

[<DebuggerStepThrough>]
member __.Bind (value: 'T option, f : 'T -> Async<'U option>) : Async<'U option> =
async {
match value with
| None -> return None
| Some result -> return! f result
}

[<DebuggerStepThrough>]
member __.Using (resource : ('T :> IDisposable), body : _ -> Async<_ option>) : Async<_ option> =
try body resource
finally if not (isNull resource) then resource.Dispose ()

[<DebuggerStepThrough>]
member x.While (guard, body : Async<_ option>) : Async<_ option> =
if guard () then
x.Bind (body, (fun () -> x.While (guard, body)))
else
x.Zero ()

[<DebuggerStepThrough>]
member x.For (sequence : seq<_>, body : 'T -> Async<unit option>) : Async<_ option> =
x.Using (sequence.GetEnumerator (), fun enum ->
x.While (enum.MoveNext, x.Delay (fun () -> body enum.Current)))

[<DebuggerStepThrough>]
member inline __.TryWith (computation : Async<'T option>, catchHandler : exn -> Async<'T option>) : Async<'T option> =
async.TryWith (computation, catchHandler)

[<DebuggerStepThrough>]
member inline __.TryFinally (computation : Async<'T option>, compensation : unit -> unit) : Async<'T option> =
async.TryFinally (computation, compensation)

let asyncMaybe = AsyncMaybeBuilder()

let inline liftAsync (computation : Async<'T>) : Async<'T option> =
async {
let! a = computation
return Some a
}

module Async =
let map (f: 'T -> 'U) (a: Async<'T>) : Async<'U> =
async {
let! a = a
return f a
}

let ignore (a: Async<'T>) : Async<unit> =
async {
let! _ = a
return ()
}

/// Creates an asynchronous workflow that runs the asynchronous workflow given as an argument at most once.
/// When the returned workflow is started for the second time, it reuses the result of the previous execution.
let cache (input : Async<'T>) =
let agent = MailboxProcessor<AsyncReplyChannel<_>>.Start <| fun agent ->
async {
let! replyCh = agent.Receive ()
let! res = input
replyCh.Reply res
while true do
let! replyCh = agent.Receive ()
replyCh.Reply res
}
async { return! agent.PostAndAsyncReply id }

type AsyncBuilder with
member __.Bind(computation: System.Threading.Tasks.Task<'a>, binder: 'a -> Async<'b>): Async<'b> =
async {
let! a = Async.AwaitTask computation
return! binder a
}

member __.ReturnFrom(computation: System.Threading.Tasks.Task<'a>): Async<'a> = Async.AwaitTask computation


module Option =
let guard (x: bool) : Option<unit> =
if x then Some() else None
Loading