Skip to content

Commit

Permalink
Improved exception handling and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
tpetricek committed Sep 23, 2012
1 parent bf8951f commit 3965cae
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 25 deletions.
40 changes: 39 additions & 1 deletion README.md
@@ -1,10 +1,48 @@
F# Formatting
=============

Markdown parser written in F#.
This project contains an F# implementation of Markdown parser and a
library that formats F# code. The formatting includes colorization, but
also generation of tooltips with types and other information similar to
those displayed in Visual Studio when reading F# code. An example can be
found on [F# snippets web site](http://wwww.fssnip.net).

**TODO:** More information will be added soon.

Using the library
-----------------

The two parts (Markdown parser and F# code formatter) are separate libraries,
but they can be easily combined together. The following script shows how to
parse Markdown document and then iterate over all CodeBlock elements (source
code samples), add formatting to them and then put them back into a document.

* [example script used at www.tryjoinads.org](https://github.com/tpetricek/TryJoinads/blob/master/tools/build.fsx)

### Using the F# formatting

To format F# code using the `FSharp.Formatting.dll` library, you need to reference
the library, open necessary namespaces and then create an instance of formatting
agent:

#r "FSharp.CodeFormat.dll"
open FSharp.CodeFormat

let fsharpCompiler = "..\\FSharp.Compiler.dll"
let asm = System.Reflection.Assembly.LoadFile(fsharpCompiler)
let formatAgent = CodeFormat.CreateAgent(asm)

Then you can use the agent repeatedly (it loads the F# compiler, so it is wise to reuse
the same instance) to format a snippet as follows:

// 'fsharpSource' contains the actual source code,
// the first argument specifies a file name (does not need
// to physically exist) and the last is compiler arguments
let snippets, errors = formatAgent.ParseSource("source.fsx", fsharpSource, "")

// This formats the snippets into HTML
let formatted = CodeFormat.FormatHtml(snippets, "ft", false, false)

Known issues
------------

Expand Down
31 changes: 28 additions & 3 deletions src/FSharp.CodeFormat.Tests/FSharp.CodeFormat.Tests.fsproj
Expand Up @@ -35,6 +35,27 @@
<PlatformTarget>x86</PlatformTarget>
<DocumentationFile>bin\Release\FSharp.CodeFormat.Tests.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\FSharp.CodeFormat.Tests.XML</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\FSharp.CodeFormat.Tests.XML</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\Microsoft.FSharp.Targets" Condition="!Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')" />
<Import Project="$(MSBuildExtensionsPath32)\..\Microsoft F#\v4.0\Microsoft.FSharp.Targets" Condition=" Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')" />
<ItemGroup>
Expand All @@ -44,9 +65,6 @@
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.CodeFormat">
<HintPath>..\FSharp.CodeFormat\bin\Debug\FSharp.CodeFormat.dll</HintPath>
</Reference>
<Reference Include="FSharp.Core">
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v4.0\FSharp.Core.dll</HintPath>
</Reference>
Expand All @@ -55,6 +73,13 @@
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FSharp.CodeFormat\FSharp.CodeFormat.fsproj">
<Name>FSharp.CodeFormat</Name>
<Project>{341ebf32-d470-4c55-99e9-55f14f7ffbb1}</Project>
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
Expand Down
16 changes: 11 additions & 5 deletions src/FSharp.CodeFormat/CodeFormatAgent.fs
Expand Up @@ -264,16 +264,19 @@ type CodeFormatAgent(fsharpCompiler) =
let! request, (chnl:AsyncReplyChannel<_>) = agent.Receive()
try
let! res, errs = processSourceCode request
chnl.Reply(res |> Array.ofList, errs)
chnl.Reply(Choice1Of2(res |> Array.ofList, errs))
with e ->
printfn "Failed %A" e
chnl.Reply(Choice2Of2(new Exception(Utilities.formatException e, e)))
})

/// Parse the source code specified by 'source', assuming that it
/// is located in a specified 'file'. Optional arguments can be used
/// to give compiler command line options and preprocessor definitions
member x.AsyncParseSource(file, source, ?options, ?defines) =
agent.PostAndAsyncReply(fun chnl -> (file, source, options, defines), chnl)
member x.AsyncParseSource(file, source, ?options, ?defines) = async {
let! res = agent.PostAndAsyncReply(fun chnl -> (file, source, options, defines), chnl)
match res with
| Choice1Of2 res -> return res
| Choice2Of2 exn -> return raise exn }

/// Parse the source code specified by 'source', assuming that it
/// is located in a specified 'file'. Optional arguments can be used
Expand All @@ -286,4 +289,7 @@ type CodeFormatAgent(fsharpCompiler) =
/// is located in a specified 'file'. Optional arguments can be used
/// to give compiler command line options and preprocessor definitions
member x.ParseSource(file, source, ?options, ?defines) =
agent.PostAndReply(fun chnl -> (file, source, options, defines), chnl)
let res = agent.PostAndReply(fun chnl -> (file, source, options, defines), chnl)
match res with
| Choice1Of2 res -> res
| Choice2Of2 exn -> raise exn
26 changes: 23 additions & 3 deletions src/FSharp.CodeFormat/FSharp.CodeFormat.fsproj
Expand Up @@ -19,12 +19,11 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<OutputPath>..\..\..\FSharp.Formatting\bin\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<DocumentationFile>
</DocumentationFile>
<DocumentationFile>..\..\..\FSharp.Formatting\bin\FSharp.CodeFormat.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
Expand All @@ -36,6 +35,27 @@
<PlatformTarget>x86</PlatformTarget>
<DocumentationFile>bin\Release\FSharp.CodeFormat.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>..\..\..\FSharp.Formatting\bin\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>..\..\..\FSharp.Formatting\bin\FSharp.CodeFormat.xml</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\FSharp.CodeFormat.XML</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\Microsoft.FSharp.Targets" Condition="!Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')" />
<Import Project="$(MSBuildExtensionsPath32)\..\Microsoft F#\v4.0\Microsoft.FSharp.Targets" Condition=" Exists('$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll')" />
<ItemGroup>
Expand Down
11 changes: 8 additions & 3 deletions src/FSharp.CodeFormat/FSharpCompiler.fs
Expand Up @@ -526,11 +526,12 @@ module SourceCodeServices =
// member GetSlotsCount : options : CheckOptions -> int
// member UntypedParseForSlot : slot:int * options : CheckOptions -> UntypedParseInfo

module Utils =
module Utilities =
open Reflection

/// Format an exception as a readable string with all information
/// (this also handles exceptions thrown by the F# language service)
/// When an exception occurs in the FSharp.Compiler.dll, we may use
/// various dynamic tricks to get the actual message from all the
/// wrapper types - this throws a readable exception
let formatException e =
let sb = new Text.StringBuilder()
let rec printe s (e:exn) =
Expand All @@ -539,11 +540,15 @@ module Utils =
if name = "Microsoft.FSharp.Compiler.ErrorLogger+Error" then
let (tup:obj) = e?Data0
Printf.bprintf sb "Compile error (%d): %s" tup?Item1 tup?Item2
elif name = "Microsoft.FSharp.Compiler.ErrorLogger+InternalError" then
Printf.bprintf sb "Internal Error message: %s" e?Data0
elif name = "Microsoft.FSharp.Compiler.ErrorLogger+ReportedError" then
let (inner:obj) = e?Data0
if inner = null then Printf.bprintf sb "Reported error is null"
else printe "Reported error" (inner?Value)
elif e.InnerException <> null then
printe "Inner exception" e.InnerException

printe "Exception" e
sb.ToString()

15 changes: 11 additions & 4 deletions src/FSharp.CodeFormat/HtmlFormatting.fs
Expand Up @@ -57,6 +57,8 @@ type FormattingContext =
{ AddLines : bool
GenerateErrors : bool
Writer : TextWriter
OpenTag : string
CloseTag : string
FormatTip : ToolTipSpans -> bool -> (ToolTipSpans -> string) -> string }

// --------------------------------------------------------------------------------------
Expand Down Expand Up @@ -148,7 +150,9 @@ let formatSnippets (ctx:FormattingContext) (snippets:Snippet[]) =
let ctx = { ctx with Writer = new StringWriter(mainStr) }

// Generate <pre> tag for the snippet
ctx.Writer.WriteLine("<pre class=\"fssnip\">")
if String.IsNullOrEmpty(ctx.OpenTag) |> not then
ctx.Writer.WriteLine(ctx.OpenTag)

let numberLength = lines.Length.ToString().Length
let linesLength = lines.Length
// Print all lines of the snippet
Expand All @@ -164,16 +168,19 @@ let formatSnippets (ctx:FormattingContext) (snippets:Snippet[]) =
if not isLast then ctx.Writer.WriteLine() )

// Close the <pre> tag for this snippet
ctx.Writer.WriteLine("</pre>")
if String.IsNullOrEmpty(ctx.CloseTag) |> not then
ctx.Writer.WriteLine(ctx.CloseTag)

ctx.Writer.Close()
yield title, mainStr.ToString() |]

/// Format snippets and return HTML for <pre> tags together
/// wtih HTML for ToolTips (to be added to the end of document)
let format addLines addErrors prefix (snippets:Snippet[]) =
let format addLines addErrors prefix openTag closeTag (snippets:Snippet[]) =
let tipf = ToolTipFormatter(prefix)
let ctx = { AddLines = addLines; GenerateErrors = addErrors
Writer = null; FormatTip = tipf.FormatTip }
Writer = null; FormatTip = tipf.FormatTip
OpenTag = openTag; CloseTag = closeTag }

// Generate main HTML for snippets
let snippets = formatSnippets ctx snippets
Expand Down
13 changes: 11 additions & 2 deletions src/FSharp.CodeFormat/Main.fs
Expand Up @@ -34,11 +34,20 @@ type CodeFormat =
/// The parameters specify prefix for HTML tags, whether lines should
/// be added to outputs and whether errors should be printed.
static member FormatHtml(snippets, prefix, addLines, addErrors) =
let snip, tip = Html.format addLines addErrors prefix snippets
CodeFormat.FormatHtml
( snippets, prefix, "<pre class=\"fssnip\">",
"</pre>", addLines, addErrors )

/// Formats the snippets parsed using the CodeFormatAgent as HTML
/// The parameters specify prefix for HTML tags, whether lines should
/// be added to outputs and whether errors should be printed.
static member FormatHtml(snippets, prefix, openTag, closeTag, addLines, addErrors) =
let snip, tip = Html.format addLines addErrors prefix openTag closeTag snippets
let snip = [| for t, h in snip -> FormattedSnippet(t, h) |]
FormattedHtml(snip, tip)

/// Formats the snippets parsed using the CodeFormatAgent as HTML
/// using the specified ID prefix and default settings.
static member FormatHtml(snippets, prefix) =
CodeFormat.FormatHtml(snippets, prefix, true, false)
CodeFormat.FormatHtml(snippets, prefix, true, false)

24 changes: 24 additions & 0 deletions src/FSharp.Formatting.sln
Expand Up @@ -9,26 +9,50 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.CodeFormat", "FSharp
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.CodeFormat.Tests", "FSharp.CodeFormat.Tests\FSharp.CodeFormat.Tests.fsproj", "{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FSharpX", "FSharpX", "{9371E533-7F81-4F20-A409-1F146F0D91EC}"
ProjectSection(SolutionItems) = preProject
FSharpX\Collections.fs = FSharpX\Collections.fs
FSharpX\StringParsing.fs = FSharpX\StringParsing.fs
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Debug|x86.ActiveCfg = Debug|x86
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Debug|x86.Build.0 = Debug|x86
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Release|Any CPU.Build.0 = Release|Any CPU
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Release|x86.ActiveCfg = Release|x86
{C44C1C05-599A-40DD-9590-465EAB8960C5}.Release|x86.Build.0 = Release|x86
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Debug|x86.ActiveCfg = Debug|x86
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Debug|x86.Build.0 = Debug|x86
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Release|Any CPU.Build.0 = Release|Any CPU
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Release|x86.ActiveCfg = Release|x86
{07DE4905-050C-4378-A039-F1EF7E1F309D}.Release|x86.Build.0 = Release|x86
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Debug|x86.ActiveCfg = Debug|x86
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Debug|x86.Build.0 = Debug|x86
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Release|Any CPU.Build.0 = Release|Any CPU
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Release|x86.ActiveCfg = Release|x86
{341EBF32-D470-4C55-99E9-55F14F7FFBB1}.Release|x86.Build.0 = Release|x86
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Debug|x86.ActiveCfg = Debug|x86
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Debug|x86.Build.0 = Debug|x86
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Release|Any CPU.Build.0 = Release|Any CPU
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Release|x86.ActiveCfg = Release|x86
{5DEBD769-D86E-4E14-ABF1-373CA91BFAA2}.Release|x86.Build.0 = Release|x86
EndGlobalSection
Expand Down
21 changes: 21 additions & 0 deletions src/FSharp.Markdown.Tests/FSharp.Markdown.Tests.fsproj
Expand Up @@ -34,6 +34,27 @@
<PlatformTarget>x86</PlatformTarget>
<DocumentationFile>bin\Release\FSharp.Markdown.Tests.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\FSharp.Markdown.Tests.XML</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\FSharp.Markdown.Tests.XML</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core" />
Expand Down

0 comments on commit 3965cae

Please sign in to comment.