tags together /// wtih HTML for ToolTips (to be added to the end of document) let format addLines addErrors prefix openTag closeTag openLinesTag closeLinesTag (snippets:Snippet[]) = - let tipf = ToolTipFormatter(prefix) - let ctx = { AddLines = addLines; GenerateErrors = addErrors - Writer = null; FormatTip = tipf.FormatTip - OpenLinesTag = openLinesTag; CloseLinesTag = closeLinesTag - OpenTag = openTag; CloseTag = closeTag } - - // Generate main HTML for snippets - let snippets = formatSnippets ctx snippets - // Generate HTML with ToolTip tags - let tipStr = StringBuilder() - tipf.WriteTipElements(new StringWriter(tipStr)) - snippets, tipStr.ToString() \ No newline at end of file + let tipf = ToolTipFormatter prefix + let ctx = { + AddLines = addLines + GenerateErrors = addErrors + Writer = null + FormatTip = tipf.FormatTip + OpenLinesTag = openLinesTag + CloseLinesTag = closeLinesTag + OpenTag = openTag + CloseTag = closeTag + } + // Generate main HTML for snippets + let snippets = formatSnippets ctx snippets + // Generate HTML with ToolTip tags + let tipStr = StringBuilder() + tipf.WriteTipElements(new StringWriter(tipStr)) + snippets, tipStr.ToString() diff --git a/src/FSharp.CodeFormat/LatexFormatting.fs b/src/FSharp.CodeFormat/LatexFormatting.fs index 414c067b8..866fac079 100644 --- a/src/FSharp.CodeFormat/LatexFormatting.fs +++ b/src/FSharp.CodeFormat/LatexFormatting.fs @@ -65,7 +65,9 @@ let rec formatTokenSpans (ctx:FormattingContext) = List.iter (function | TokenKind.Operator -> @"\ops" | TokenKind.Preprocessor -> @"\prep" | TokenKind.String -> @"\str" - | TokenKind.TypeOrModule -> @"\ltyp" + | TokenKind.Module + | TokenKind.ValueType + | TokenKind.ReferenceType -> @"\ltyp" | TokenKind.Function -> @"\lfun" | TokenKind.Pattern -> @"\lpat" | TokenKind.MutableVar -> @"\lvar" diff --git a/src/FSharp.CodeFormat/Pervasive.fs b/src/FSharp.CodeFormat/Pervasive.fs new file mode 100644 index 000000000..b4ab79037 --- /dev/null +++ b/src/FSharp.CodeFormat/Pervasive.fs @@ -0,0 +1,158 @@ +[] +module internal FSharp.CodeFormat.Pervasive + +open System +open System.Diagnostics +open Microsoft.FSharp.Compiler +open Microsoft.FSharp.Compiler.SourceCodeServices + +[ ] +type AsyncMaybeBuilder () = + + [ ] + member __.Return value : Async<'T option> = Some value |> async.Return + + [ ] + member __.ReturnFrom value : Async<'T option> = value + + [ ] + member __.ReturnFrom (value: 'T option) : Async<'T option> = async.Return value + + [ ] + member __.Zero () : Async = Some () |> async.Return + + [ ] + member __.Delay (f : unit -> Async<'T option>) : Async<'T option> = async.Delay f + + [ ] + member __.Combine (r1, r2 : Async<'T Option>) : Async<'T option> = async { + let! r1' = r1 + match r1' with + | None -> return None + | Some () -> return! r2 + } + + [ ] + 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 + } + + [ ] + 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' + } + + [ ] + 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 + } + + [ ] + member __.Using (resource : ('T :> IDisposable), body : _ -> Async<_ option>) : Async<_ option> = + try body resource + finally if not (isNull resource) then resource.Dispose () + + [ ] + member x.While (guard, body : Async<_ option>) : Async<_ option> = + if guard () then + x.Bind (body, (fun () -> x.While (guard, body))) + else + x.Zero () + + [ ] + member x.For (sequence : seq<_>, body : 'T -> Async ) : Async<_ option> = + x.Using (sequence.GetEnumerator (), fun enum -> + x.While (enum.MoveNext, x.Delay (fun () -> body enum.Current))) + + [ ] + member inline __.TryWith (computation : Async<'T option>, catchHandler : exn -> Async<'T option>) : Async<'T option> = + async.TryWith (computation, catchHandler) + + [ ] + 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 + } + + /// 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 >.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 CheckResults = + | Ready of (FSharpParseFileResults * FSharpCheckFileResults) option + | StillRunning of Async<(FSharpParseFileResults * FSharpCheckFileResults) option> + +type FSharpChecker with + + member this.ParseAndCheckDocument(filePath: string, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = + let parseAndCheckFile = async { + let! parseResults, checkFileAnswer = this.ParseAndCheckFileInProject(filePath, 0, sourceText, options) + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> None + | FSharpCheckFileAnswer.Succeeded checkFileResults -> Some (parseResults, checkFileResults) + } + + let tryGetFreshResultsWithTimeout () : Async = async { + try let! worker = Async.StartChild (parseAndCheckFile, 2000) + let! result = worker + return Ready result + with :? TimeoutException -> return StillRunning parseAndCheckFile + } + + let bindParsedInput (results: (FSharpParseFileResults * FSharpCheckFileResults) option) = + match results with + | Some (parseResults, checkResults) -> + match parseResults.ParseTree with + | Some parsedInput -> Some (parseResults, parsedInput, checkResults) + | None -> None + | None -> None + + if allowStaleResults then + async { + let! freshResults = tryGetFreshResultsWithTimeout() + + let! results = + match freshResults with + | Ready x -> async.Return x + | StillRunning worker -> + async { + match allowStaleResults, this.TryGetRecentCheckResultsForFile(filePath, options) with + | true, Some (parseResults, checkFileResults, _) -> + return Some (parseResults, checkFileResults) + | _ -> + return! worker + } + return bindParsedInput results + } + else parseAndCheckFile |> Async.map bindParsedInput \ No newline at end of file diff --git a/src/FSharp.CodeFormat/SourceCode.fs b/src/FSharp.CodeFormat/SourceCode.fs index 6520f579f..518eb7224 100644 --- a/src/FSharp.CodeFormat/SourceCode.fs +++ b/src/FSharp.CodeFormat/SourceCode.fs @@ -12,35 +12,43 @@ namespace FSharp.CodeFormat type ToolTipSpans = list /// A tool tip span can be emphasized text, plain text `Literal` or a line brak -and ToolTipSpan = - | Emphasis of ToolTipSpans - | Literal of string - | HardLineBreak +and ToolTipSpan = + | Emphasis of ToolTipSpans + | Literal of string + | HardLineBreak -/// Classifies tokens reported by the F# lexer and F# PowerTools -/// (PowerTools provide additional information e.g. whether a variable -/// is mutable, etc.) +/// Classifies tokens reported by the FCS [ ] -type TokenKind = - | Keyword - | String - | Comment - | Identifier - | Inactive - | Number - | Operator - | Preprocessor - | TypeOrModule - | Function - | Pattern - | MutableVar - | Printf - | Escaped - | Default +type TokenKind = + | Keyword + | String + | Comment + | Identifier + | Inactive + | Number + | Operator + | Punctuation + | Preprocessor + | Module + | ReferenceType + | ValueType + | Interface + | TypeArgument + | Property + | Enumeration + | UnionCase + | Function + | Pattern + | MutableVar + | Disposable + | Printf + | Escaped + | Default + /// Represents a kind of error reported from the F# compiler (warning or error) [ ] -type ErrorKind = +type ErrorKind = | Error | Warning @@ -48,7 +56,7 @@ type ErrorKind = /// the compiler (`Token`), this also includes `Error` (wrapping the underlined /// tokens), `Omitted` for the special `[omit:...]` tags and `Output` for the special /// `[output:...]` tag -type TokenSpan = +type TokenSpan = | Token of TokenKind * string * ToolTipSpans option | Error of ErrorKind * string * TokenSpans | Omitted of string * string diff --git a/src/FSharp.CodeFormat/ToolTipReader.fs b/src/FSharp.CodeFormat/ToolTipReader.fs index 15399e26f..6a562dd23 100644 --- a/src/FSharp.CodeFormat/ToolTipReader.fs +++ b/src/FSharp.CodeFormat/ToolTipReader.fs @@ -58,7 +58,11 @@ let private formatElement = function yield Literal " " yield Emphasis [Literal (msg) ] yield HardLineBreak ] - + | FSharpToolTipElement.SingleParameter(_paramType,_doc,_name) -> + [ yield ToolTipSpan.Literal _paramType + yield ToolTipSpan.HardLineBreak + yield! formatComment _doc + ] | FSharpToolTipElement.CompositionError(err) -> [] /// Format entire tool tip as a value of type ToolTipSpans diff --git a/src/FSharp.CodeFormat/app.config b/src/FSharp.CodeFormat/app.config index 84919544e..c15bb22a9 100644 --- a/src/FSharp.CodeFormat/app.config +++ b/src/FSharp.CodeFormat/app.config @@ -1,15 +1,24 @@ - \ No newline at end of file +- -- - - + - - True - + + +True ++ + + +True ++ + + + diff --git a/src/FSharp.CodeFormat/paket.references b/src/FSharp.CodeFormat/paket.references index 8f4c61640..0a1dca0be 100644 --- a/src/FSharp.CodeFormat/paket.references +++ b/src/FSharp.CodeFormat/paket.references @@ -1,3 +1,2 @@ FSharp.Compiler.Service -FSharpVSPowerTools.Core -FSharp.Core \ No newline at end of file +FSharp.Core diff --git a/src/FSharp.Formatting.CommandTool/App.config b/src/FSharp.Formatting.CommandTool/App.config deleted file mode 100644 index c5e99b644..000000000 --- a/src/FSharp.Formatting.CommandTool/App.config +++ /dev/null @@ -1,63 +0,0 @@ - -True ++ + - \ No newline at end of file diff --git a/src/FSharp.Formatting.CommandTool/FSharp.Formatting.CommandTool.fsproj b/src/FSharp.Formatting.CommandTool/FSharp.Formatting.CommandTool.fsproj index b6c32c948..c2a57e2bf 100644 --- a/src/FSharp.Formatting.CommandTool/FSharp.Formatting.CommandTool.fsproj +++ b/src/FSharp.Formatting.CommandTool/FSharp.Formatting.CommandTool.fsproj @@ -4,24 +4,21 @@- -- - - -- -- - -- -- - -- - - -- - -- - - -- - - -- - - -- - - -- - - -- - - -True -- - Debug AnyCPU -2.0 d30f7f2b-a4e3-4a07-a1bd-ed3eb21768f8 Exe FSharp.FormattingCLI fsformatting -v4.5 +v4.6.1 .NETFramework true -4.4.0.0 +4.4.1.0 FSharp.FormattingCLI FSFCLI ..\..\ -true -true -full +portable false false ..\..\bin\ @@ -33,7 +30,7 @@true - pdbonly +portable false true ..\..\bin\ @@ -50,9 +47,9 @@+
Hello
" - html |> should contain "1:" - html |> should contain "2:" + html |> shouldContainText "Hello
" + html |> shouldContainText "1:" + html |> shouldContainText "2:" html |> shouldNotContainText "3:" [Hello
" - html |> should contain "1:" - html |> should contain "2:" + html |> shouldContainText "Hello
" + html |> shouldContainText "1:" + html |> shouldContainText "2:" html |> shouldNotContainText "3:" [FsLib.Nested.MyType
is just an int
"
+ files.["fslib-nested.html"] |> shouldContainText "You will notice that FsLib.Nested.MyType
is just an int
"
// Check that a link to MyType exists when using Full Name of the type in a inline code
- files.["fslib-nested.html"] |> should contain "You will notice that OtherType
is just an int
"
+ files.["fslib-nested.html"] |> shouldContainText "You will notice that OtherType
is just an int
"
// Check that a link to a type with a duplicated name is not created when using Logical name only
- files.["fslib-nested.html"] |> should contain "DuplicatedTypeName
is duplicated so it should no add a cross-type link"
+ files.["fslib-nested.html"] |> shouldContainText "DuplicatedTypeName
is duplicated so it should no add a cross-type link"
// Check that a link to a type with a duplicated name is not created when using Logical name only
- files.["fslib-nested.html"] |> should contain "InexistentTypeName
does not exists so it should no add a cross-type link"
+ files.["fslib-nested.html"] |> shouldContainText "InexistentTypeName
does not exists so it should no add a cross-type link"
// Check that a link to a module is created when using Logical Name only
- files.["fslib-duplicatedtypename.html"] |> should contain "This type name will be duplicated in Nested
"
+ files.["fslib-duplicatedtypename.html"] |> shouldContainText "This type name will be duplicated in Nested
"
// Check that a link to a type with a duplicated name is created when using full name
- files.["fslib-nested-duplicatedtypename.html"] |> should contain "This type has the same name as FsLib.DuplicatedTypeName
"
\ No newline at end of file
+ files.["fslib-nested-duplicatedtypename.html"] |> shouldContainText "This type has the same name as FsLib.DuplicatedTypeName
"
+
+
+let runtest testfn =
+ try testfn ()
+ with e -> printfn "Error -\n%s\n\nStackTrace -\n%s\n\n\TargetSite -\n%s\n" e.Message e.StackTrace e.TargetSite.Name
+#if INTERACTIVE
+;;
+printfn "Metadata generates cross-type links for Inline Code"
+runtest ``Metadata generates cross-type links for Inline Code``;;
+
+printfn "Metadata generates cross-type links for Indirect Links"
+runtest ``Metadata generates cross-type links for Indirect Links``;;
+
+printfn "MetadataFormat test FsLib1"
+runtest ``MetadataFormat test FsLib1``;;
+
+printfn "MetadataFormat omit works without markdown"
+runtest ``MetadataFormat omit works without markdown``;;
+
+printfn "MetadataFormat generates module link in nested types"
+runtest ``MetadataFormat generates module link in nested types``;;
+runtest ``MetadataFormat processes C# properties on types and includes xml comments in docs``;;
+
+printfn "MetadataFormat handles c# dlls"
+runtest ``MetadataFormat handles c# dlls``;;
+
+printfn "MetadataFormat highlights code snippets in Markdown comments"
+runtest ``MetadataFormat highlights code snippets in Markdown comments``;;
+
+printfn "MetadataFormat process XML comments in two sample F# assemblies"
+runtest ``MetadataFormat process XML comments in two sample F# assemblies``;;
+
+printfn "MetadataFormat works on sample Deedle assembly"
+runtest ``MetadataFormat works on sample Deedle assembly``;;
+
+printfn "MetadataFormat works on two sample F# assemblies"
+runtest ``MetadataFormat works on two sample F# assemblies``;;
+
+printfn "MetadataFormat test that csharp (publiconly) support works"
+runtest ``MetadataFormat test that csharp (publiconly) support works``;;
+
+printfn "MetadataFormat test that cref generation works"
+runtest ``MetadataFormat test that cref generation works``;;
+
+#endif
\ No newline at end of file
diff --git a/tests/FSharp.MetadataFormat.Tests/app.config b/tests/FSharp.MetadataFormat.Tests/app.config
new file mode 100644
index 000000000..c15bb22a9
--- /dev/null
+++ b/tests/FSharp.MetadataFormat.Tests/app.config
@@ -0,0 +1,24 @@
+
+