# initial support for F# #237

Merged
merged 9 commits into from Jun 6, 2019
+862 −223

## Conversation

Projects
None yet
4 participants
Member

### brettfo commented May 25, 2019 • edited

 tl;dr - Editing/running F# snippets in Try .NET and displaying diagnostics. Oh boy, is this a giant pile of hacks. I'm creating this PR so early in the process to get feedback on how to better architect these changes, because right now I'm rather ashamed of what I've done. List of sins and why I committed them (pun intended): Workspace servers: Rather than try to factor out all the calls to RoslynWorkspaceServer and properly abstract them, I created WorkspaceServerMultiplexer which simply looks for the suffix .fsproj on the workspace type to redirect to FSharpWorkspaceServer; otherwise it defaults to RoslynWorkspaceServer. As I understand it, this will only work for on-disk workspaces which just happen to set their type to the project file path (see item 1 below). I've mostly plumbed through a Language property, and otherwise auto-detected it from the .fsproj suffix for the workspace servers. In an attempt to reuse as much as possible (meaning less plumbing), I hijacked various functions to explicitly look for F#-specific file extensions and then bail in a different direction. See BufferInliningTransformer.cs, FileExtensions.cs, and SourceTextExtensions.cs. A method in BufferInliningTransformer.cs has been made virtual to allow for F#-specific overriding. Markdig doesn't understand code fences of the type fsharp, so to get it to appropriately classify F# blocks as AnnotatedCodeBlocks, I had to lie and say it was C#. The proper fix for this should be adding F# support to Markdig. Turns out it's not Markdig, but this repo; I found and updated the language parsing. F# can't perform compilations and analysis on purely in-memory data structures like Roslyn can, so I introduced RedirectedPackage which creates a directory under %TEMP%, copies everything from the parent Package, and does the text substitution and all work there. (This also explains why I do source path mapping for F# diagnostics in Library.fs.) After compilation the bin/\$(Configuration) directory is then copied back to the 'real' location to allow execution. See item 6.A below. Things that still need to be done: Allow a workspace to properly state it's preferred server: RoslynWorkspaceServer or FSharpWorkspaceServer. Parameterize the hijacked methods and call sites to make the separation cleaner. See item 2 above. Cleaned up as much as possible for this PR. Will be cleaned up even more when #254 is integrated. Implement completion in FSharpWorkspaceServer.cs (future PR). Implement signature help in FSharpWorkspaceServer.cs (future PR). Add F# tests and fix whatever other tests I most certainly broke. (Long lead) update the F# libraries to allow in-memory compilation and analysis. A. In the short term we could likely hijack the call to dotnet build and pass an additional property, /p:OutputPath=path\to\original\project\bin. Uninteresting note: The C# runner uses #region/#endregion tags to designate which parts of the file to display in the editor. F# doesn't have the concept of regions but other tools cheat and use the specially-formatted comments, //#region and //#endregion, so I also opted for that approach.

Open

### jonsequitur reviewed May 28, 2019

 { internal class FSharpMethods { private const string FSharpRegionStart = "//#region";

#### jonsequitur May 28, 2019

Collaborator

"//#region" [](start = 49, length = 11)

Is this ideal? Any suggestions for making it more natural in F#?

#### brettfo May 28, 2019

Author Member

Not really; F# doesn't have the equivalent of #region or any concept of a code block. The closest thing we have is computation expressions, but that would require the writer of the documentation/sample to define a new builder for each span of code. E.g., the backing code could look like this:

type DemoRegionBuilder() =
member __.Zero() = ()

// ... elsewhere
let demo_region_1 = new DemoRegionBuilder()
let demo_region_2 = new DemoRegionBuilder()
// .. repeat for every equivalent of #region demo_region_X that would be found in C#

[<EntryPoint>]
let main(args: string[]) =
let _ =
demo_region_1 {
printfn "hello, world" // this line appears in the web page
}
0

With the markdown looking like this:

   fsharp --project ./MySampleProject.fsproj --source-file ./MySampleFile.fs --region demo_region_1
printfn "hello, world" // this line appears in the web page
  

Adding @KevinRansom to see if he has any additional insight, but I'm still leaning towards specially-formatted comments, because that's essentially what C#/VB #regions are anyways.

### jonsequitur reviewed Jun 6, 2019

 i <- i + delta buf.ToString() let private newlineifyErrorString (message:string) = message.Replace(newlineProxy, Environment.NewLine)

Collaborator

"newlineify" 😆

#### brettfo Jun 6, 2019

Author Member

Slightly shorter than newlineificize.

### jonsequitur reviewed Jun 6, 2019

 @@ -113,7 +113,7 @@ private static void AddSourceFileOption(Command csharp) var projectFiles = directoryAccessor.GetAllFilesRecursively() .Where(file => { return directoryAccessor.GetFullyQualifiedPath(file.Directory).FullName == rootDirectory.FullName && file.Extension == ".csproj"; return directoryAccessor.GetFullyQualifiedPath(file.Directory).FullName == rootDirectory.FullName && (file.Extension == ".csproj" || file.Extension == ".fsproj");

#### jonsequitur Jun 6, 2019

Collaborator

There's a potential inconsistency here in that I can do this and it will be valid:

c# --project ./something.fsproj


#### brettfo Jun 6, 2019

Author Member

My plan (hope?) was to merge as-is to feature/fsharp (where this PR is targeted) and then do a merge in from master to get @colombod's changes that add a language parameter to the workspace object and clean up the various hacks I added around detecting the language.

### brettfo added some commits May 24, 2019

 initial support for F# 
 afe3b28 
 plumb through language name 
 8fd37ae 
 allow code blocks of type fsharp 
 958a109 
 consolidate region and buffer extraction 
 65cce9f 
 add buffer/region extraction tests 
 f1d41f9 

### brettfo changed the title [WIP] initial support for F#initial support for F#Jun 6, 2019

 expand F# math demo 
 e961f53 

### brettfoforce-pushed the brettfo:fsharp branch from 7398019 to 7b53b44Jun 6, 2019

 format F# code with proper indentation levels 
 73d176a 

### colombod requested changes Jun 6, 2019

WorkspaceServer/Servers/WorkspaceServerMultiplexer.cs Outdated

### colombod requested changes Jun 6, 2019

WorkspaceServer/Servers/WorkspaceServerMultiplexer.cs Outdated
 use language switch 
 5b4b8ea 
 remove unnecessary interface property 
 6754a0b 

Details