├── .gitattributes ├── .gitignore ├── .paket ├── paket.bootstrapper.exe └── paket.targets ├── ProtoWorkspace.Tests ├── AssemblyInfo.fs ├── Library1.fs ├── ProtoWorkspace.Tests.fsproj ├── Script.fsx └── paket.references ├── ProtoWorkspace.sln ├── ProtoWorkspace ├── BufferTracker.fs ├── CodeService.fsx ├── Commands.fs ├── Constants.fs ├── ConvertProject.fsx ├── Converters.fs ├── Environment.fs ├── Extensions.fs ├── HostServices.fs ├── LanguageService.fs ├── Loaders.fs ├── MSBuildConfig.fs ├── MSBuildEval.fsx ├── MSBuildProjectSystem.fs ├── Prelude.fs ├── ProjectConfig.fs ├── ProjectFileInfo.fs ├── ProtoWorkspace.fsproj ├── Scripts │ ├── load-project-debug.fsx │ ├── load-project-release.fsx │ ├── load-references-debug.fsx │ └── load-references-release.fsx ├── Services │ ├── BraceMatching.fs │ ├── Highlighting.fs │ ├── QuickInfo.fs │ └── ServiceInterfaces.fs ├── SolutionFileInfo.fs ├── Text.fs ├── Workspace.fs ├── WorkspaceServices.fs ├── paket.references └── slntest.fsx ├── README.md ├── data ├── TestSln.sln ├── module_001.fs ├── module_002.fs ├── projects │ ├── Library1 │ │ ├── AssemblyInfo.fs │ │ ├── Library1.fs │ │ ├── Library1.fsproj │ │ └── Script.fsx │ └── Library2 │ │ ├── AssemblyInfo.fs │ │ ├── Library1.fs │ │ ├── Library2.fsproj │ │ └── Script.fsx ├── script_001.fsx ├── script_002.fsx ├── sig_001.fsi └── sig_002.fsi ├── paket.dependencies ├── paket.lock └── reference.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /.paket/paket.bootstrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsharp-editing/ProtoWorkspace/4e2509a5da4789ccd673328dbb8ffec3b0801b8a/.paket/paket.bootstrapper.exe -------------------------------------------------------------------------------- /.paket/paket.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | true 8 | $(MSBuildThisFileDirectory) 9 | $(MSBuildThisFileDirectory)..\ 10 | /Library/Frameworks/Mono.framework/Commands/mono 11 | mono 12 | 13 | 14 | 15 | $(PaketToolsPath)paket.exe 16 | $(PaketToolsPath)paket.bootstrapper.exe 17 | "$(PaketExePath)" 18 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 19 | "$(PaketBootStrapperExePath)" $(PaketBootStrapperCommandArgs) 20 | $(MonoPath) --runtime=v4.0.30319 $(PaketBootStrapperExePath) $(PaketBootStrapperCommandArgs) 21 | 22 | $(MSBuildProjectDirectory)\paket.references 23 | $(MSBuildStartupDirectory)\paket.references 24 | $(MSBuildProjectFullPath).paket.references 25 | $(PaketCommand) restore --references-files "$(PaketReferences)" 26 | $(PaketBootStrapperCommand) 27 | 28 | RestorePackages; $(BuildDependsOn); 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ProtoWorkspace.Tests/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace.Tests.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [] 37 | [] 38 | [] 39 | 40 | do 41 | () -------------------------------------------------------------------------------- /ProtoWorkspace.Tests/Library1.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace.Tests 2 | 3 | type Class1() = 4 | member this.X = "F#" 5 | -------------------------------------------------------------------------------- /ProtoWorkspace.Tests/ProtoWorkspace.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 57e880fe-e9f7-409d-81c7-2f50a62967d4 9 | Library 10 | ProtoWorkspace.Tests 11 | ProtoWorkspace.Tests 12 | v4.6.1 13 | 4.4.0.0 14 | true 15 | ProtoWorkspace.Tests 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | 3 26 | bin\Debug\ProtoWorkspace.Tests.XML 27 | 28 | 29 | pdbonly 30 | true 31 | true 32 | bin\Release\ 33 | TRACE 34 | 3 35 | bin\Release\ProtoWorkspace.Tests.XML 36 | 37 | 38 | 39 | 40 | True 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ProtoWorkspace 54 | {31c9d72f-6b95-4280-9abf-b97e421ff49d} 55 | True 56 | 57 | 58 | 59 | 11 60 | 61 | 62 | 63 | 64 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 65 | 66 | 67 | 68 | 69 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 70 | 71 | 72 | 73 | 74 | 81 | 82 | 83 | 84 | 85 | ..\packages\FsCheck\lib\net45\FsCheck.dll 86 | True 87 | True 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | ..\packages\NUnit\lib\net45\nunit.framework.dll 97 | True 98 | True 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /ProtoWorkspace.Tests/Script.fsx: -------------------------------------------------------------------------------- 1 | // Learn more about F# at http://fsharp.org. See the 'F# Tutorial' project 2 | // for more guidance on F# programming. 3 | 4 | #load "Library1.fs" 5 | open ProtoWorkspace.Tests 6 | 7 | // Define your library scripting code here 8 | 9 | -------------------------------------------------------------------------------- /ProtoWorkspace.Tests/paket.references: -------------------------------------------------------------------------------- 1 | NUnit 2 | FsCheck -------------------------------------------------------------------------------- /ProtoWorkspace.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ProtoWorkspace", "ProtoWorkspace\ProtoWorkspace.fsproj", "{31C9D72F-6B95-4280-9ABF-B97E421FF49D}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2E286A42-DBB8-4C4C-A9F2-DCA7FA73C44A}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | paket.dependencies = paket.dependencies 12 | paket.lock = paket.lock 13 | README.md = README.md 14 | Reference.md = Reference.md 15 | EndProjectSection 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{B8CADD49-9C2A-4983-A09A-6DC8F6704A44}" 18 | ProjectSection(SolutionItems) = preProject 19 | data\module_001.fs = data\module_001.fs 20 | data\module_002.fs = data\module_002.fs 21 | data\script_001.fsx = data\script_001.fsx 22 | data\script_002.fsx = data\script_002.fsx 23 | data\sig_001.fsi = data\sig_001.fsi 24 | data\sig_002.fsi = data\sig_002.fsi 25 | EndProjectSection 26 | EndProject 27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projects", "Projects", "{C0D364E2-E301-48BF-AB78-83F99C39FF4A}" 28 | ProjectSection(SolutionItems) = preProject 29 | data\projects\Library1\Library1.fsproj = data\projects\Library1\Library1.fsproj 30 | data\projects\Library2\Library2.fsproj = data\projects\Library2\Library2.fsproj 31 | EndProjectSection 32 | EndProject 33 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ProtoWorkspace.Tests", "ProtoWorkspace.Tests\ProtoWorkspace.Tests.fsproj", "{57E880FE-E9F7-409D-81C7-2F50A62967D4}" 34 | EndProject 35 | Global 36 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 37 | Debug|Any CPU = Debug|Any CPU 38 | Release|Any CPU = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 41 | {31C9D72F-6B95-4280-9ABF-B97E421FF49D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {31C9D72F-6B95-4280-9ABF-B97E421FF49D}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {31C9D72F-6B95-4280-9ABF-B97E421FF49D}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {31C9D72F-6B95-4280-9ABF-B97E421FF49D}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {57E880FE-E9F7-409D-81C7-2F50A62967D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {57E880FE-E9F7-409D-81C7-2F50A62967D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {57E880FE-E9F7-409D-81C7-2F50A62967D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {57E880FE-E9F7-409D-81C7-2F50A62967D4}.Release|Any CPU.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | GlobalSection(NestedProjects) = preSolution 54 | {C0D364E2-E301-48BF-AB78-83F99C39FF4A} = {B8CADD49-9C2A-4983-A09A-6DC8F6704A44} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /ProtoWorkspace/BufferTracker.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | open System 4 | open System.Text 5 | open System.Collections.Generic 6 | open Microsoft.FSharp.Compiler 7 | open Microsoft.FSharp.Compiler.Range 8 | 9 | [] 10 | type EditorBuffer = 11 | { Text : string 12 | Range : range 13 | IsDirty : bool 14 | Encoding : Encoding 15 | LastChangeTime : DateTime 16 | ViewCount : int } 17 | static member Create text range isDirty encoding lastChangeTime = 18 | { Text = text 19 | Range = range 20 | IsDirty = isDirty 21 | Encoding = encoding 22 | LastChangeTime = lastChangeTime 23 | ViewCount = 1 } 24 | 25 | type IBufferTracker = 26 | abstract MapEditorBuffers : (KeyValuePair -> 'a) -> seq<'a> 27 | abstract TryFindEditorBuffer : string -> EditorBuffer option 28 | abstract TryGetBufferText : string -> string option 29 | abstract BufferChanged : string IEvent 30 | abstract BufferClosed : string IEvent 31 | -------------------------------------------------------------------------------- /ProtoWorkspace/CodeService.fsx: -------------------------------------------------------------------------------- 1 | System.IO.Directory.SetCurrentDirectory __SOURCE_DIRECTORY__ 2 | #r "System.Threading.Tasks" 3 | #load "scripts/load-references-release.fsx" 4 | #r "bin/release/protoworkspace.dll" 5 | 6 | open System 7 | open System.IO 8 | open System.Collections.Generic 9 | open Microsoft.CodeAnalysis 10 | open Microsoft.FSharp.Compiler.SourceCodeServices 11 | open Microsoft.Build 12 | open Microsoft.Build.Framework 13 | open Microsoft.Build.Evaluation 14 | open Microsoft.Build.Execution 15 | open Microsoft.Build.Utilities 16 | open System.Xml 17 | open System.Xml.Linq 18 | open ProtoWorkspace 19 | open System.Threading 20 | 21 | 22 | let printsq sqs = sqs |> Seq.iter (printfn "%A") 23 | 24 | 25 | //let library1path = "../data/projects/Library1/Library1.fsproj" |> Path.GetFullPath 26 | let library1path = "ProtoWorkspace.fsproj" |> Path.GetFullPath 27 | let library2path = "../data/projects/Library2/Library2.fsproj" |> Path.GetFullPath 28 | 29 | 30 | let xdoc = (library1path |> File.ReadAllText |> XDocument.Parse).Root 31 | 32 | let fswork = new FSharpWorkspace() 33 | 34 | 35 | let lib1info = (ProjectFileInfo.create library1path) |> ProjectFileInfo.toProjectInfo fswork 36 | 37 | let lib1proj = fswork.AddProject lib1info 38 | ;; 39 | lib1proj.Documents 40 | |> Seq.iter (fun doc -> printfn "%s - %s" doc.Name doc.FilePath) 41 | 42 | 43 | lib1proj.Documents |> Seq.find(fun doc -> doc.Name = "Library1") 44 | |> fun doc -> 45 | printfn "%s" doc.Name 46 | let text = doc.GetTextAsync().Result 47 | 48 | text.ToString() 49 | ;; 50 | 51 | let checker = FSharpChecker.Create() 52 | 53 | let fsopts = lib1proj.ToFSharpProjectOptions fswork 54 | ;; 55 | let checkDoc (doc:Document) = async { 56 | let! version = doc.GetTextVersionAsync() |> Async.AwaitTask 57 | let! text = doc.GetTextAsync() |> Async.AwaitTask 58 | let! parseResults, checkAnswer = checker.ParseAndCheckFileInProject(doc.FilePath,0,text.ToString(),fsopts) 59 | return 60 | match checkAnswer with 61 | | FSharpCheckFileAnswer.Succeeded res -> Some res 62 | | res -> None 63 | } 64 | 65 | 66 | lib1proj.Documents |> Seq.find(fun doc -> doc.Name = "ProjectFileInfo") 67 | |> fun doc -> 68 | let results = checkDoc doc |> Async.RunSynchronously 69 | if results.IsSome then 70 | (results.Value.GetAllUsesOfAllSymbolsInFile() |> Async.RunSynchronously) 71 | |> Array.iter ^ fun sym -> printfn "%s" sym.Symbol.FullName 72 | else 73 | printfn "didn't get back symbol results" 74 | 75 | 76 | -------------------------------------------------------------------------------- /ProtoWorkspace/Commands.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | open System 4 | open System.IO 5 | open System.Composition 6 | open Microsoft.CodeAnalysis 7 | open Newtonsoft.Json 8 | open ProtoWorkspace.Text 9 | 10 | [] 11 | /// Contains the literals for the strings representing Editing Command Endpoints 12 | module Command = 13 | let [] GotoDefinition = "/gotodefinition" 14 | let [] FindSymbols = "/findsymbols" 15 | let [] UpdateBuffer = "/updatebuffer" 16 | let [] ChangeBuffer = "/changebuffer" 17 | let [] CodeCheck = "/codecheck" 18 | let [] FilesChanged = "/filesChanged" 19 | let [] FormatAfterKeystroke = "/formatAfterKeystroke" 20 | let [] FormatRange = "/formatRange" 21 | let [] CodeFormat = "/codeformat" 22 | let [] Highlight = "/highlight" 23 | let [] AutoComplete = "/autocomplete" 24 | let [] FindImplementations = "/findimplementations" 25 | let [] FindUsages = "/findusages" 26 | let [] GotoFile = "/gotofile" 27 | let [] GotoRegion = "/gotoregion" 28 | let [] NavigateUp = "/navigateup" 29 | let [] NavigateDown = "/navigatedown" 30 | let [] TypeLookup = "/typelookup" 31 | let [] GetCodeAction = "/getcodeactions" 32 | let [] RunCodeAction = "/runcodeaction" 33 | let [] Rename = "/rename" 34 | let [] SignatureHelp = "/signatureHelp" 35 | let [] MembersTree = "/currentfilemembersastree" 36 | let [] MembersFlat = "/currentfilemembersasflat" 37 | let [] TestCommand = "/gettestcontext" 38 | let [] Metadata = "/metadata" 39 | let [] PackageSource = "/packagesource" 40 | let [] PackageSearch = "/packagesearch" 41 | let [] PackageVersion = "/packageversion" 42 | let [] WorkspaceInformation = "/projects" 43 | let [] ProjectInformation = "/project" 44 | let [] FixUsings = "/fixusings" 45 | let [] CheckAliveStatus = "/checkalivestatus" 46 | let [] CheckReadyStatus = "/checkreadystatus" 47 | let [] StopServer = "/stopserver" 48 | let [] Open = "/open" 49 | let [] Close = "/close" 50 | let [] Diagnostics = "/diagnostics" 51 | 52 | 53 | type IRequest = interface end 54 | type IRequestHandler = interface end 55 | 56 | 57 | [] 58 | /// MEF Exports an IRequestHandler 59 | type CommandHandlerAttribute(commandName:string, language:string) = 60 | inherit ExportAttribute(typeof) 61 | member __.CommandName = commandName 62 | member __.Language = language 63 | 64 | 65 | type CommandDescriptor<'Request,'Response> (commandName:string) = 66 | member val RequestType = typeof<'Request> with get 67 | member val ResponseType = typeof<'Response> with get 68 | member val CommandName = commandName with get 69 | 70 | 71 | [] 72 | /// MEF Exports an IRequest 73 | type EditorCommandAttribute (commandName:string, request:Type,response:Type) = 74 | inherit ExportAttribute(typeof) 75 | member val RequestType = request with get 76 | member val ResponseType = response with get 77 | member val CommandName = commandName with get 78 | 79 | 80 | type Request () = 81 | let mutable fileName = "" 82 | 83 | interface IRequest 84 | 85 | [)>] 86 | member val Line : int = 0 with get, set 87 | 88 | [)>] 89 | member val Column : int = 0 with get, set 90 | member val Buffer : string = "" with get, set 91 | member val Changes : LinePositionSpanTextChange seq = Seq.empty with get, set 92 | 93 | member __.FileName 94 | with get () = 95 | fileName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) 96 | and set v = if isNull v then fileName <- String.Empty else fileName <- v 97 | 98 | 99 | type RequestHandler<'Request,'Response> = 100 | inherit IRequestHandler 101 | abstract member Handle: request:'Request -> 'Response Async 102 | 103 | 104 | type IAggregateResponse = 105 | abstract member Merge : response: IAggregateResponse -> IAggregateResponse 106 | 107 | 108 | type QuickFixResponse () = 109 | member val QuickFixes : QuickFix seq = Seq.empty with get, set 110 | 111 | new (quickFixes:QuickFix seq) as self = 112 | QuickFixResponse() then 113 | self.QuickFixes <- quickFixes 114 | 115 | member self.Merge (response:QuickFixResponse) = 116 | Seq.append self.QuickFixes response.QuickFixes 117 | |> QuickFixResponse 118 | 119 | interface IAggregateResponse with 120 | member self.Merge response = 121 | self.Merge (response :?> QuickFixResponse) 122 | :> IAggregateResponse 123 | 124 | 125 | 126 | type ModifiedFileResponse (fileName:string) = 127 | member val FileName = fileName with get, set 128 | member val Buffer = "" with get, set 129 | member val Changes : LinePositionSpanTextChange seq = Seq.empty with get, set 130 | new () = ModifiedFileResponse String.Empty 131 | 132 | 133 | // File Open 134 | 135 | type FileOpenResponse () = 136 | interface IAggregateResponse with 137 | member __.Merge response = response 138 | 139 | [,typeof)>] 140 | type FileOpenRequest() = 141 | inherit Request() 142 | 143 | // File Close 144 | 145 | type FileCloseResponse () = 146 | interface IAggregateResponse with 147 | member __.Merge response = response 148 | 149 | [,typeof)>] 150 | type FileCloseRequest() = 151 | inherit Request() 152 | 153 | 154 | // Rename 155 | 156 | type RenameResponse () = 157 | member val Changes : ModifiedFileResponse seq = Seq.empty with get, set 158 | member val ErrorMessage = "" with get, set 159 | 160 | [,typeof)>] 161 | type RenameRequest () = 162 | inherit Request () 163 | /// When true, return just the text changes. 164 | member val WantsTextChanges = false with get, set 165 | /// When true, apply changes immediately on the server. 166 | member val ApplyTextChanges = true with get, set 167 | member val RenameTo = "" with get, set 168 | 169 | 170 | // Buffer 171 | 172 | [,typeof)>] 173 | type UpdateBufferRequest () = 174 | inherit Request() 175 | /// Instead of updating the buffer from the editor, 176 | /// set this to allow updating from disk 177 | member val FromDisk = false with get, set 178 | 179 | [,typeof)>] 180 | type ChangeBufferRequest () = 181 | interface IRequest 182 | 183 | member val NewText : string = "" with get, set 184 | member val FileName : string = "" with get, set 185 | [)>] 186 | member val StartLine : int = 0 with get, set 187 | [)>] 188 | member val StartColumn : int = 0 with get, set 189 | [)>] 190 | member val EndLine : int = 0 with get, set 191 | [)>] 192 | member val EndColumn : int = 0 with get, set 193 | 194 | 195 | // Signature Help 196 | 197 | [,typeof)>] 198 | type SignatureHelpRequest () = inherit Request() 199 | 200 | 201 | // Fix Usings 202 | 203 | type FixUsingsResponse () = 204 | member val Buffer = "" with get, set 205 | member val AmbiguousResults : QuickFix seq = Seq.empty with get, set 206 | member val Changes : LinePositionSpanTextChange seq = Seq.empty with get, set 207 | 208 | 209 | [,typeof)>] 210 | type FixUsingsRequest () = 211 | inherit Request() 212 | member val WantsTextChanges = false with get, set 213 | member val ApplyTextChanges = true with get, set 214 | 215 | 216 | 217 | // AutoComplete 218 | 219 | type AutoCompleteResponse () = 220 | /// The text to be "completed", that is, the text that will be inserted in the 221 | member val CompletionText = "" with get, set 222 | member val Description = "" with get, set 223 | /// The text that should be displayed in the auto-complete UI. 224 | member val DisplayText = "" with get, set 225 | member val RequiredNamespaceImport = "" with get, set 226 | member val MethodHandler = "" with get, set 227 | member val ReturnType = "" with get, set 228 | member val Snippet = "" with get, set 229 | member val Kind = "" with get, set 230 | 231 | override self.Equals (other:obj) = 232 | if isNull other then false else 233 | match other with 234 | | :? AutoCompleteResponse as other -> 235 | self.DisplayText = other.DisplayText 236 | && self.Snippet = other.Snippet 237 | | _ -> false 238 | 239 | override self.GetHashCode() = 240 | 17 * hash self.DisplayText 241 | + (if isNotNull self.Snippet then 31 * hash self.Snippet else 0) 242 | 243 | 244 | [,typeof)>] 245 | type AutoCompleteRequest () = 246 | inherit Request() 247 | let mutable wordToComplete = "" 248 | 249 | member __.WordToComplete 250 | with get () = wordToComplete ?|? "" 251 | and set v = wordToComplete <- v 252 | 253 | /// Specifies whether to return the code documentation for 254 | /// each and every returned autocomplete result. 255 | member val WantDocumentationForEveryCompletionResult = false with get, set 256 | 257 | /// Specifies whether to return importable types. Defaults to 258 | /// false. Can be turned off to get a small speed boost. 259 | member val WantImportableTypes = false with get, set 260 | 261 | /// Returns a 'method header' for working with parameter templating. 262 | member val WantMethodHeader = false with get, set 263 | 264 | /// Returns a snippet that can be used by common snippet libraries 265 | /// to provide parameter and type parameter placeholders 266 | member val WantSnippet = false with get, set 267 | 268 | /// Returns the return type 269 | member val WantReturnType = false with get, set 270 | 271 | /// Returns the kind (i.e Method, Property, Field) 272 | member val WantKind = false with get, set 273 | 274 | -------------------------------------------------------------------------------- /ProtoWorkspace/Constants.fs: -------------------------------------------------------------------------------- 1 |  2 | namespace ProtoWorkspace 3 | open System 4 | 5 | module Constants = 6 | let [] FSharpProjectGuidStr = "F2A71F9B-5D33-465A-A702-920D77279786" 7 | let FSharpProjectGuid = Guid FSharpProjectGuidStr 8 | let [] SolutionFolderGuidStr = "2150E333-8FDC-42A3-9474-1A3956D46DE8" 9 | let SolutionFolderGuid = Guid SolutionFolderGuidStr 10 | let [] FSharpLanguageName = "F#" 11 | let [] FSharpContentTypeName = "F#" 12 | 13 | // Common Constants 14 | let [] Name = "Name" 15 | let [] None = "None" 16 | let [] Reference = "Reference" 17 | 18 | // Platform Constants 19 | let [] X86 = "x86" 20 | let [] X64 = "x64" 21 | let [] AnyCPU = "AnyCPU" 22 | 23 | // BuildAction Constants 24 | let [] Compile = "Compile" 25 | let [] Content = "Content" 26 | let [] Resource = "Resource" 27 | let [] EmbeddedResource = "EmbeddedResource" 28 | 29 | // CopyToOutputDirectory Constants 30 | let [] Never = "Never" 31 | let [] Always = "Always" 32 | let [] PreserveNewest = "PreserveNewest" 33 | 34 | // DebugType Constants 35 | let [] PdbOnly = "PdbOnly" 36 | let [] Full = "Full" 37 | 38 | // OutputType Constants 39 | let [] Exe = "Exe" 40 | let [] Winexe = "Winexe" 41 | let [] Library = "Library" 42 | let [] Module = "Module" 43 | 44 | // XML Attribute Name Constants 45 | let [] DefaultTargets = "DefaultTargets" 46 | let [] ToolsVersion = "ToolsVersion" 47 | let [] Include = "Include" 48 | let [] Condition = "Condition" 49 | 50 | // MSBuild XML Element Constants 51 | 52 | let [] Project = "Project" 53 | let [] ItemGroup = "ItemGroup" 54 | let [] PropertyGroup = "PropertyGroup" 55 | let [] ProjectReference = "ProjectReference" 56 | 57 | // XML Property Constants (found in PropertyGroups) 58 | let [] AssemblyName = "AssemblyName" 59 | let [] RootNamespace = "RootNamespace" 60 | let [] Configuration = "Configuration" 61 | let [] Platform = "Platform" 62 | let [] SchemaVersion = "SchemaVersion" 63 | let [] ProjectGuid = "ProjectGuid" 64 | let [] ProjectType = "ProjectType" 65 | let [] OutputType = "OutputType" 66 | let [] TargetFrameworkVersion = "TargetFrameworkVersion" 67 | let [] TargetFrameworkProfile = "TargetFrameworkProfile" 68 | let [] AutoGenerateBindingRedirects = "AutoGenerateBindingRedirects" 69 | let [] TargetFSharpCoreVersion = "TargetFSharpCoreVersion" 70 | let [] DebugSymbols = "DebugSymbols" 71 | let [] DebugType = "DebugType" 72 | let [] Optimize = "Optimize" 73 | let [] Tailcalls = "Tailcalls" 74 | let [] OutputPath = "OutputPath" 75 | let [] CompilationConstants = "DefineConstants" 76 | let [] WarningLevel = "WarningLevel" 77 | let [] PlatformTarget = "PlatformTarget" 78 | let [] DocumentationFile = "DocumentationFile" 79 | let [] Prefer32Bit = "Prefer32Bit" 80 | let [] OtherFlags = "OtherFlags" 81 | 82 | // XML Elements 83 | let [] CopyToOutputDirectory = "CopyToOutputDirectory" 84 | let [] HintPath = "HintPath" 85 | let [] Private = "Private" 86 | let [] SpecificVersion = "SpecificVersion" 87 | let [] Link = "Link" 88 | let [] Paket = "Paket" 89 | let [] XmlDecl = @"" 90 | let [] Xmlns = "http://schemas.microsoft.com/developer/msbuild/2003" 91 | 92 | 93 | [] 94 | /// MSBuild Properties 95 | module Property = 96 | let [] AllowUnsafeBlocks = "AllowUnsafeBlocks" 97 | let [] AssemblyName = "AssemblyName" 98 | let [] AssemblyOriginatorKeyFile = "AssemblyOriginatorKeyFile" 99 | let [] BuildProjectReferences = "BuildProjectReferences" 100 | let [] DefineConstants = "DefineConstants" 101 | let [] DesignTimeBuild = "DesignTimeBuild" 102 | let [] DocumentationFile = "DocumentationFile" 103 | let [] LangVersion = "LangVersion" 104 | let [] OutputType = "OutputType" 105 | let [] MSBuildExtensionsPath = "MSBuildExtensionsPath" 106 | let [] ProjectGuid = "ProjectGuid" 107 | let [] ProjectName = "ProjectName" 108 | let [] ResolveReferenceDependencies = "ResolveReferenceDependencies" 109 | let [] SignAssembly = "SignAssembly" 110 | let [] SolutionDir = "SolutionDir" 111 | let [] TargetFrameworkMoniker = "TargetFrameworkMoniker" 112 | let [] TargetPath = "TargetPath" 113 | let [] VisualStudioVersion = "VisualStudioVersion" 114 | 115 | 116 | [] 117 | /// MSBuild Project Target Names 118 | module TargetName = 119 | 120 | let [] ResolveReferences = "ResolveReferences" 121 | 122 | 123 | [] 124 | /// MSBuild Project Item Names 125 | module ItemName = 126 | let [] Analyzer = "Analyzer" 127 | let [] Compile = "Compile" 128 | let [] None = "None" 129 | let [] ProjectReference = "ProjectReference" 130 | let [] ReferencePath = "ReferencePath" 131 | 132 | [] 133 | /// MSBuild Project Metadata Names 134 | module MetadataName = 135 | let [] FullPath = "FullPath" 136 | let [] Project = "Project" 137 | let [] ReferenceSourceTarget = "ReferenceSourceTarget" 138 | -------------------------------------------------------------------------------- /ProtoWorkspace/ConvertProject.fsx: -------------------------------------------------------------------------------- 1 | System.IO.Directory.SetCurrentDirectory __SOURCE_DIRECTORY__ 2 | 3 | #r "System.Threading.Tasks" 4 | #load "scripts/load-references-release.fsx" 5 | #r "bin/release/protoworkspace.dll" 6 | 7 | open System 8 | open System.IO 9 | open System.Collections.Generic 10 | open Microsoft.FSharp.Compiler.SourceCodeServices 11 | open Microsoft.CodeAnalysis 12 | open System.Xml 13 | open System.Xml.Linq 14 | open ProtoWorkspace 15 | open ProtoWorkspace.ProjectFileInfo 16 | open System.Threading 17 | 18 | let internal toFSharpProjectOptions (workspace: 'a when 'a :> Workspace) (projInfo: ProjectInfo): FSharpProjectOptions = 19 | let projectStore = Dictionary() 20 | 21 | let rec generate (projInfo:ProjectInfo) : FSharpProjectOptions = 22 | let getProjectRefs (projInfo:ProjectInfo) : (string * FSharpProjectOptions) [] = 23 | projInfo.ProjectReferences 24 | |> Seq.choose ^ fun pref -> workspace.CurrentSolution.TryGetProject pref.ProjectId 25 | |> Seq.map ^ fun proj -> 26 | let proj = proj.ToProjectInfo() 27 | if projectStore.ContainsKey proj.Id then (proj.OutputFilePath, projectStore.[proj.Id]) 28 | else 29 | let fsinfo = generate proj 30 | projectStore.Add (proj.Id, fsinfo) 31 | (proj.OutputFilePath, fsinfo) 32 | |> Array.ofSeq 33 | { ProjectFileName = projInfo.FilePath 34 | ProjectFileNames = 35 | projInfo.Documents 36 | |> Seq.map ^ fun doc -> doc.FilePath 37 | |> Array.ofSeq 38 | OtherOptions = [||] 39 | ReferencedProjects = getProjectRefs projInfo 40 | IsIncompleteTypeCheckEnvironment = false 41 | UseScriptResolutionRules = false 42 | LoadTime = System.DateTime.Now 43 | UnresolvedReferences = None 44 | } 45 | generate projInfo 46 | 47 | type ProjectInfo with 48 | member self.ToFSharpProjectOptions(workspace: 'a when 'a :> Workspace): FSharpProjectOptions = 49 | toFSharpProjectOptions workspace self 50 | 51 | type Project with 52 | member self.ToProjectInfo() = 53 | ProjectInfo.Create 54 | (self.Id, self.Version, self.Name, self.AssemblyName, self.Language, self.FilePath, 55 | outputFilePath = self.OutputFilePath, 56 | projectReferences = self.ProjectReferences, 57 | metadataReferences = self.MetadataReferences, 58 | analyzerReferences = self.AnalyzerReferences, 59 | documents = (self.Documents |> Seq.map ^ fun doc -> doc.ToDocumentInfo()), 60 | additionalDocuments = (self.AdditionalDocuments |> Seq.map ^ fun doc -> doc.ToDocumentInfo()), 61 | compilationOptions = self.CompilationOptions, 62 | parseOptions = self.ParseOptions, 63 | isSubmission = self.IsSubmission) 64 | member self.ToFSharpProjectOptions(workspace: 'a when 'a :> Workspace): FSharpProjectOptions = 65 | self.ToProjectInfo().ToFSharpProjectOptions workspace 66 | 67 | 68 | let fswork = new FSharpWorkspace() 69 | 70 | let protopath = "ProtoWorkspace.fsproj" |> Path.GetFullPath 71 | 72 | let protoinfo = (ProjectFileInfo.create protopath) |> ProjectFileInfo.toProjectInfo fswork 73 | 74 | let protoproj = fswork.AddProject protoinfo 75 | ;; 76 | let protoOpts = protoinfo.ToFSharpProjectOptions fswork 77 | 78 | -------------------------------------------------------------------------------- /ProtoWorkspace/Converters.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module ProtoWorkspace.Converters 3 | 4 | open System 5 | open System.Collections.Immutable 6 | open System.Threading.Tasks 7 | open Microsoft.CodeAnalysis 8 | open Microsoft.CodeAnalysis.Text 9 | open Microsoft.FSharp.Compiler 10 | open Microsoft.FSharp.Compiler.SourceCodeServices 11 | open Microsoft.FSharp.Compiler.Range 12 | open Newtonsoft.Json 13 | 14 | 15 | let fsharpRangeToTextSpan (sourceText:SourceText, range:range) = 16 | // Roslyn TextLineCollection is zero-based, F# range lines are one-based 17 | let startPosition = sourceText.Lines.[range.StartLine - 1].Start + range.StartColumn 18 | let endPosition = sourceText.Lines.[range.EndLine - 1].Start + range.EndColumn 19 | TextSpan (startPosition, endPosition - startPosition) 20 | 21 | let getTaskAction (computation:Async) = 22 | // Shortcut due to nonstandard way of converting Async to Task 23 | let action () = 24 | try 25 | computation |> Async.RunSynchronously 26 | with ex -> 27 | Assert.Exception ^ ex.GetBaseException() 28 | raise ^ ex.GetBaseException() 29 | Action action 30 | 31 | let getCompletedTaskResult (task:'Result Task)= 32 | if task.Status = TaskStatus.RanToCompletion then 33 | task.Result 34 | else 35 | Assert.Exception ^ task.Exception.GetBaseException() 36 | raise ^ task.Exception.GetBaseException() 37 | 38 | let supportedDiagnostics () = 39 | // We are constructing our own descriptors at run-time. Compiler service is already doing error formatting and localization. 40 | let dummyDescriptor = DiagnosticDescriptor("0", String.Empty, String.Empty, String.Empty, DiagnosticSeverity.Error, true, null, null) 41 | ImmutableArray.Create dummyDescriptor 42 | 43 | let convertError(error: FSharpErrorInfo, location: Location) = 44 | let id = "FS" + error.ErrorNumber.ToString "0000" 45 | let emptyString = LocalizableString.op_Implicit "" 46 | let description = LocalizableString.op_Implicit error.Message 47 | let severity = if error.Severity = FSharpErrorSeverity.Error then DiagnosticSeverity.Error else DiagnosticSeverity.Warning 48 | let descriptor = DiagnosticDescriptor(id, emptyString, description, error.Subcategory, severity, true, emptyString, String.Empty, null) 49 | Diagnostic.Create(descriptor, location) 50 | 51 | 52 | 53 | type ZeroBasedIndexConverter() = 54 | inherit JsonConverter() 55 | 56 | override __.CanConvert (objectType:Type) = 57 | objectType 58 | |>( (=) typeof 59 | |?| (=) typeof 60 | |?| (=) typeof 61 | |?| (=) typeof 62 | ) 63 | 64 | override __.ReadJson (reader:JsonReader, objectType:Type, existingValue:obj, serializer:JsonSerializer) = 65 | if reader.TokenType = JsonToken.Null then null 66 | elif objectType = typeof then 67 | serializer.Deserialize reader :> obj 68 | elif objectType = typeof then 69 | serializer.Deserialize reader 70 | |> Seq.map ^ fun x -> x - 1 71 | :> obj 72 | elif objectType = typeof then 73 | let result = serializer.Deserialize reader 74 | if result.HasValue then 75 | result.Value :> obj 76 | else 77 | null 78 | else 79 | null 80 | 81 | (* Omnisharp has a configuration on whether to use zerobasedindices or not 82 | if (Configuration.ZeroBasedIndices) 83 | { 84 | return serializer.Deserialize(reader, objectType); 85 | } 86 | *) 87 | 88 | 89 | override __.WriteJson(writer:JsonWriter, value: obj, serializer:JsonSerializer) = 90 | if isNull value then 91 | serializer.Serialize(writer,null) 92 | else 93 | let objectType = value.GetType() 94 | let results = 95 | if objectType = typeof then 96 | let results = value :?> int[] 97 | for i=0 to results.Length-1 do 98 | results.[i] <- results.[i] + 1 99 | results :> obj 100 | elif objectType = typeof then 101 | let results = value :?> int seq 102 | results |> Seq.map ^ fun x -> x + 1 103 | :> obj 104 | elif objectType = typeof then 105 | let result = value :?> int Nullable 106 | if result.HasValue then 107 | result.Value + 1 :> obj 108 | else 109 | null 110 | else 111 | null 112 | serializer.Serialize(writer,results) 113 | 114 | 115 | (* 116 | 117 | let FSharpRangeToTextSpan(sourceText: SourceText, range: range) = 118 | // Roslyn TextLineCollection is zero-based, F# range lines are one-based 119 | let startPosition = sourceText.Lines.[range.StartLine - 1].Start + range.StartColumn 120 | let endPosition = sourceText.Lines.[range.EndLine - 1].Start + range.EndColumn 121 | TextSpan(startPosition, endPosition - startPosition) 122 | 123 | 124 | 125 | 126 | 127 | *) 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /ProtoWorkspace/Environment.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | [] 4 | module Environment = 5 | open System 6 | 7 | /// Are we running on the Mono platform? 8 | let runningOnMono = 9 | try 10 | System.Type.GetType "Mono.Runtime" <> null 11 | with _ -> false 12 | 13 | let ``namewith.to`` = "" 14 | 15 | /// Target framework (used to find the right version of F# binaries) 16 | type FSharpTargetFramework = 17 | | NET_2_0 18 | | NET_3_0 19 | | NET_3_5 20 | | NET_4_0 21 | | NET_4_5 22 | | NET_4_6 23 | 24 | type FSharpCompilerVersion = 25 | /// F# 2.0 - 4.0.0.0 26 | | FSharp_2_0 27 | /// F# 3.0 - 4.3.0.0 28 | | FSharp_3_0 29 | /// F# 3.1 - 4.3.1.0 30 | | FSharp_3_1 31 | /// F# 4.0 - 4.4.0.0 32 | | FSharp_4_0 33 | /// F# 4.1 - 4.4.1.0 34 | | FSharp_4_1 35 | 36 | override x.ToString() = 37 | match x with 38 | | FSharp_2_0 -> "4.0.0.0" 39 | | FSharp_3_0 -> "4.3.0.0" 40 | | FSharp_3_1 -> "4.3.1.0" 41 | | FSharp_4_0 -> "4.4.0.0" 42 | | FSharp_4_1 -> "4.4.1.0" 43 | 44 | /// The current requested language version can be overridden by the user using environment variable. 45 | static member LatestKnown = 46 | match System.Environment.GetEnvironmentVariable "FSHARP_PREFERRED_VERSION" with 47 | | "4.0.0.0" -> FSharp_2_0 48 | | "4.3.0.0" -> FSharp_3_0 49 | | "4.3.1.0" -> FSharp_3_1 50 | | "4.4.0.0" -> FSharp_4_0 51 | | "4.4.1.0" -> FSharp_4_1 52 | | null | _ -> FSharp_4_0 53 | 54 | let maxPath = 260 55 | let maxDataLength = System.Text.UTF32Encoding().GetMaxByteCount maxPath 56 | let KEY_WOW64_DEFAULT = 0x0000 57 | let KEY_WOW64_32KEY = 0x0200 58 | let HKEY_LOCAL_MACHINE = UIntPtr 0x80000002u 59 | let KEY_QUERY_VALUE = 0x1 60 | let REG_SZ = 1u 61 | -------------------------------------------------------------------------------- /ProtoWorkspace/Extensions.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module ProtoWorkspace.Extensions 3 | open System 4 | open System.IO 5 | open System.Threading.Tasks 6 | open System.Collections.Generic 7 | open Microsoft.Extensions.Logging 8 | open Microsoft.CodeAnalysis 9 | open Microsoft.CodeAnalysis.Text 10 | open Microsoft.FSharp.Compiler 11 | open Microsoft.FSharp.Compiler.SourceCodeServices 12 | 13 | 14 | type AsyncBuilder with 15 | member self.Bind (task: 'a Task, fn: 'a -> 'b Async) = 16 | self.Bind (Async.AwaitTask task, fn) 17 | 18 | member self.Bind (task: Task, fn: unit -> unit Async) = 19 | self.Bind (Async.AwaitTask task, fn) 20 | 21 | 22 | type ILogger with 23 | member self.LogInfofn msg = Printf.ksprintf (fun s -> self.LogInformation(s, [||])) msg 24 | member self.LogCriticalfn msg = Printf.ksprintf (fun s -> self.LogCritical(s, [||])) msg 25 | member self.LogDebugfn msg = Printf.ksprintf (fun s -> self.LogDebug(s, [||])) msg 26 | member self.LogTracefn msg = Printf.ksprintf (fun s -> self.LogTrace(s, [||])) msg 27 | member self.LogErrorfn msg = Printf.ksprintf (fun s -> self.LogError(s, [||])) msg 28 | member self.LogWarningfn msg = Printf.ksprintf (fun s -> self.LogWarning(s, [||])) msg 29 | 30 | 31 | (* CodeAnalysis Extensions *) 32 | 33 | type TextSpan with 34 | /// Compares two instances of Microsoft.CodeAnalysis.Text.TextSpan 35 | static member CompareTo (a:TextSpan,b:TextSpan) = a.CompareTo b 36 | 37 | type Document with 38 | 39 | member self.ToDocumentInfo () = 40 | DocumentInfo.Create 41 | ( id=self.Id 42 | , name=self.Name 43 | , folders=self.Folders 44 | , sourceCodeKind=self.SourceCodeKind 45 | , loader=FileTextLoader(self.FilePath,Text.Encoding.UTF8) 46 | , filePath=self.FilePath 47 | ) 48 | 49 | 50 | type TextDocument with 51 | 52 | member self.ToDocumentInfo () = 53 | DocumentInfo.Create 54 | ( id=self.Id 55 | , name=self.Name 56 | , folders=self.Folders 57 | , loader=FileTextLoader(self.FilePath,Text.Encoding.UTF8) 58 | , filePath=self.FilePath 59 | ) 60 | 61 | 62 | type Solution with 63 | 64 | /// Get a project inside the solution using the project's name 65 | member self.GetProject projectName = 66 | self.Projects |> Seq.find(fun proj -> proj.Name = projectName) 67 | 68 | /// Try to get a project inside the solution using the project's name 69 | member self.TryGetProject projectName = 70 | self.Projects |> Seq.tryFind(fun proj -> proj.Name = projectName) 71 | 72 | /// Try to get a project inside the solution using the project's id 73 | member self.TryGetProject (projId:ProjectId) = 74 | if self.ContainsProject projId then Some (self.GetProject projId) else None 75 | 76 | /// Sequence of DocumentInfo for all source files and addtional documents 77 | /// from all of the projects within the solution 78 | member self.AllDocuments = 79 | self.Projects |> Seq.collect ^ fun proj -> 80 | Seq.append 81 | (proj.Documents |> Seq.map ^ fun doc -> doc.ToDocumentInfo()) 82 | (proj.AdditionalDocuments |> Seq.map ^ fun doc -> doc.ToDocumentInfo()) 83 | 84 | 85 | member self.Directory = (FileInfo self.FilePath).Directory.FullName 86 | 87 | 88 | type Project with 89 | 90 | member self.GetDocument docName = 91 | self.Documents |> Seq.find (fun doc -> doc.Name = docName) 92 | 93 | member self.TryGetDocument docName = 94 | self.Documents |> Seq.tryFind (fun doc -> doc.Name = docName) 95 | 96 | 97 | member self.ToProjectInfo () = 98 | ProjectInfo.Create 99 | ( self.Id 100 | , self.Version 101 | , self.Name 102 | , self.AssemblyName 103 | , self.Language 104 | , self.FilePath 105 | , outputFilePath = self.OutputFilePath 106 | , projectReferences = self.ProjectReferences 107 | , metadataReferences = self.MetadataReferences 108 | , analyzerReferences = self.AnalyzerReferences 109 | , documents = (self.Documents |> Seq.map(fun doc -> doc.ToDocumentInfo())) 110 | , additionalDocuments = (self.AdditionalDocuments |> Seq.map(fun doc -> doc.ToDocumentInfo())) 111 | , compilationOptions = self.CompilationOptions 112 | , parseOptions = self.ParseOptions 113 | , isSubmission = self.IsSubmission 114 | ) 115 | 116 | 117 | let internal toFSharpProjectOptions (workspace:'a :> Workspace) (projInfo:ProjectInfo): FSharpProjectOptions = 118 | let projectStore = Dictionary() 119 | 120 | let rec generate (projInfo:ProjectInfo) : FSharpProjectOptions = 121 | let getProjectRefs (projInfo:ProjectInfo): (string * FSharpProjectOptions)[] = 122 | projInfo.ProjectReferences 123 | |> Seq.choose (fun pref -> workspace.CurrentSolution.TryGetProject pref.ProjectId) 124 | |> Seq.map(fun proj -> 125 | let proj = proj.ToProjectInfo() 126 | if projectStore.ContainsKey(proj.Id) then 127 | (proj.OutputFilePath, projectStore.[proj.Id]) 128 | else 129 | let fsinfo = generate proj 130 | projectStore.Add(proj.Id,fsinfo) 131 | (proj.OutputFilePath, fsinfo) 132 | )|> Array.ofSeq 133 | 134 | { ProjectFileName = projInfo.FilePath 135 | ProjectFileNames = projInfo.Documents |> Seq.map(fun doc -> doc.FilePath) |> Array.ofSeq 136 | OtherOptions = [||] 137 | ReferencedProjects = getProjectRefs projInfo 138 | IsIncompleteTypeCheckEnvironment = false 139 | UseScriptResolutionRules = false 140 | LoadTime = System.DateTime.Now 141 | UnresolvedReferences = None 142 | } 143 | let fsprojOptions = generate projInfo 144 | projectStore.Clear() 145 | fsprojOptions 146 | 147 | 148 | 149 | type ProjectInfo with 150 | 151 | member self.ToFSharpProjectOptions (workspace:'a :> Workspace) : FSharpProjectOptions = 152 | toFSharpProjectOptions workspace self 153 | 154 | 155 | type Project with 156 | 157 | member self.ToFSharpProjectOptions (workspace:'a :> Workspace) : FSharpProjectOptions = 158 | self.ToProjectInfo().ToFSharpProjectOptions workspace 159 | 160 | 161 | 162 | 163 | 164 | type Workspace with 165 | 166 | member self.ProjectDictionary() = 167 | let dict = Dictionary<_,_>() 168 | self.CurrentSolution.Projects 169 | |> Seq.iter ^ fun proj -> dict.Add(proj.FilePath,proj.Id) 170 | dict 171 | 172 | 173 | member self.ProjectPaths() = 174 | self.CurrentSolution.Projects |> Seq.map(fun proj -> proj.FilePath) 175 | 176 | 177 | member self.GetProjectIdFromPath path : ProjectId option = 178 | let dict = self.ProjectDictionary() 179 | Dict.tryFind path dict 180 | 181 | 182 | /// checks the workspace for projects located at the provided paths. 183 | /// returns a mapping of the projectId and path of projects inside the workspace 184 | /// and a list of the paths to projects the workspace doesn't include 185 | member self.GetProjectIdsFromPaths paths = 186 | let dict = self.ProjectDictionary() 187 | let pathsInside,pathsOutside = paths |> List.ofSeq |> List.partition ^ fun path -> dict.ContainsKey path 188 | let idmap = pathsInside |> Seq.map ^ fun path -> dict.[path] 189 | idmap, pathsOutside 190 | 191 | 192 | member self.GetProject projectName = 193 | self.CurrentSolution.GetProject 194 | 195 | (* MSBuild Extensions *) 196 | 197 | open Microsoft.Build 198 | open Microsoft.Build.Evaluation 199 | open Microsoft.Build.Execution 200 | 201 | 202 | 203 | type ProjectInstance with 204 | 205 | member self.TryGetPropertyValue propertyName = 206 | let value = self.GetPropertyValue propertyName 207 | if String.IsNullOrEmpty value then None else Some value 208 | -------------------------------------------------------------------------------- /ProtoWorkspace/HostServices.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace.HostServices 2 | 3 | open ProtoWorkspace 4 | open System 5 | open System.Collections.Generic 6 | open System.Reflection 7 | open System.Composition 8 | open System.Composition.Hosting.Core 9 | open System.Collections.Immutable 10 | open Microsoft.CodeAnalysis 11 | open Microsoft.CodeAnalysis.Host 12 | open Microsoft.CodeAnalysis.Host.Mef 13 | 14 | // based on - https://gist.github.com/praeclarum/953629b2f80860e54747 15 | 16 | type FSharpHostLanguageService (workspace:Workspace) = 17 | inherit HostLanguageServices() 18 | 19 | override __.Language = Constants.FSharpLanguageName 20 | override __.WorkspaceServices with get () = workspace.Services 21 | override __.GetService<'a when 'a :> ILanguageService>() : 'a = Unchecked.defaultof<'a> 22 | 23 | 24 | 25 | type FSharpHostWorkspaceService (workspace:Workspace,baseServices:HostWorkspaceServices) = 26 | inherit HostWorkspaceServices() 27 | 28 | let languageService = FSharpHostLanguageService workspace 29 | 30 | override __.GetService<'a when 'a :> IWorkspaceService >() = 31 | baseServices.GetService<'a>() 32 | 33 | override __.HostServices with get() = workspace.Services.HostServices 34 | 35 | override __.Workspace = workspace 36 | 37 | override __.IsSupported languageName = languageName = Constants.FSharpLanguageName 38 | 39 | override __.SupportedLanguages = seq [Constants.FSharpLanguageName] 40 | 41 | override __.GetLanguageServices _ = languageService :> HostLanguageServices 42 | 43 | override __.FindLanguageServices filter = base.FindLanguageServices filter 44 | 45 | 46 | type FSharpHostService () = 47 | inherit HostServices() 48 | let baseWorkspace = new AdhocWorkspace() 49 | 50 | override __.CreateWorkspaceServices workspace = 51 | FSharpHostWorkspaceService(workspace,baseWorkspace.Services) :> HostWorkspaceServices 52 | 53 | 54 | type IHostServicesProvider = 55 | abstract Assemblies : Assembly ImmutableArray 56 | 57 | [] 58 | type HostServicesAggregator [] ([] hostServicesProviders : seq) = 59 | let builder = ImmutableHashSet.CreateBuilder() 60 | 61 | do 62 | for asm in MefHostServices.DefaultAssemblies do 63 | builder.Add asm |> ignore 64 | for provider in hostServicesProviders do 65 | for asm in provider.Assemblies do 66 | builder.Add asm |> ignore 67 | 68 | let assemblies = builder.ToImmutableArray() 69 | member __.CreateHostServices() = MefHostServices.Create assemblies 70 | 71 | 72 | type MefValueProvider<'a> (item:'a) = 73 | inherit ExportDescriptorProvider() 74 | 75 | // override self.GetExportDescriptors (contract:CompositionContract, descriptorAcessor:DependencyAccessor) = 76 | // seq { if contract.ContractType = typeof<'a> then 77 | // yield ExportDescriptorPromise 78 | // ( contract, String.Empty, true, 79 | // (fun () -> Seq.empty : CompositionDependency seq), 80 | // (fun deps -> 81 | // ExportDescriptor.Create 82 | // ( CompositeActivator (fun context operation -> item :> obj) 83 | // , Dictionary()) 84 | // ) 85 | // ) 86 | // else yield! Seq.empty 87 | // } 88 | 89 | override self.GetExportDescriptors (contract:CompositionContract, _ ) = 90 | seq { if contract.ContractType = typeof<'a> then 91 | yield ExportDescriptorPromise 92 | ( contract, String.Empty, true, 93 | (fun () -> Seq.empty : CompositionDependency seq), 94 | (fun _ -> ExportDescriptor.Create(CompositeActivator(fun _ _ -> item :> obj),Dictionary<_,_>())) 95 | ) 96 | else yield! Seq.empty 97 | } 98 | 99 | type MefValueProvider = 100 | static member From<'a> (value:'a) = MefValueProvider<'a> value -------------------------------------------------------------------------------- /ProtoWorkspace/LanguageService.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.LanguageService 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.Runtime.InteropServices 6 | open System.Linq 7 | open System.IO 8 | 9 | open Microsoft.FSharp.Compiler 10 | open Microsoft.FSharp.Compiler.SourceCodeServices 11 | open Microsoft.CodeAnalysis 12 | open Microsoft.CodeAnalysis.Editing 13 | 14 | 15 | 16 | type internal FSharpLanguageService (workspace:FSharpWorkspace) = 17 | 18 | static let optionsCache = Dictionary() 19 | static member GetOptions(projectId: ProjectId) = 20 | if optionsCache.ContainsKey projectId then 21 | Some optionsCache.[projectId] 22 | else 23 | None 24 | 25 | // member this.SyncProject(project: AbstractProject, projectContext: IWorkspaceProjectContext, site: IProjectSite) = 26 | // let updatedFiles = site.SourceFilesOnDisk() 27 | // let workspaceFiles = project.GetCurrentDocuments() |> Seq.map(fun file -> file.FilePath) 28 | // 29 | // for file in updatedFiles do if not(workspaceFiles.Contains(file)) then projectContext.AddSourceFile(file) 30 | // for file in workspaceFiles do if not(updatedFiles.Contains(file)) then projectContext.RemoveSourceFile(file) 31 | 32 | member this.ContentTypeName = Constants.FSharpContentTypeName 33 | member this.LanguageName = Constants.FSharpLanguageName 34 | member this.RoslynLanguageName = Constants.FSharpLanguageName 35 | 36 | // member this.LanguageServiceId = new Guid(FSharpConstants.languageServiceGuidString) 37 | member this.DebuggerLanguageId = DebuggerEnvironment.GetLanguageID() 38 | 39 | member this.CreateContext(_,_,_,_,_) = raise ^ System.NotImplementedException() 40 | 41 | // member this.SetupNewTextView(textView) = 42 | // 43 | //// let workspace = this.Package.ComponentModel.GetService() 44 | // 45 | // // FSROSLYNTODO: Hide navigation bars for now. Enable after adding tests 46 | // workspace.Options <- workspace.Options.WithChangedOption(NavigationBarOptions.ShowNavigationBar, FSharpCommonConstants.FSharpLanguageName, false) 47 | // 48 | // match textView.GetBuffer() with 49 | // | (VSConstants.S_OK, textLines) -> 50 | // let filename = VsTextLines.GetFilename textLines 51 | // match VsRunningDocumentTable.FindDocumentWithoutLocking(package.RunningDocumentTable,filename) with 52 | // | Some (hier, _) -> 53 | // if IsScript(filename) then 54 | // let editorAdapterFactoryService = this.Package.ComponentModel.GetService() 55 | // let fileContents = VsTextLines.GetFileContents(textLines, editorAdapterFactoryService) 56 | // this.SetupStandAloneFile(filename, fileContents, workspace, hier) 57 | // else 58 | // match hier with 59 | // | :? IProvideProjectSite as siteProvider -> this.SetupProjectFile(siteProvider, workspace) 60 | // | _ -> () 61 | // | _ -> () 62 | // | _ -> () 63 | 64 | // member this.SetupProjectFile(siteProvider: IProvideProjectSite, workspace: VisualStudioWorkspaceImpl) = 65 | // let site = siteProvider.GetProjectSite() 66 | // let projectGuid = Guid(site.ProjectGuid) 67 | // let projectFileName = site.ProjectFileName() 68 | // let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectFileName) 69 | // 70 | // let options = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(site, site.ProjectFileName()) 71 | // if not (optionsCache.ContainsKey(projectId)) then 72 | // optionsCache.Add(projectId, options) 73 | // 74 | // match workspace.ProjectTracker.GetProject(projectId) with 75 | // | null -> 76 | // let projectContextFactory = this.Package.ComponentModel.GetService(); 77 | // let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) 78 | // 79 | // let projectContext = projectContextFactory.CreateProjectContext(FSharpCommonConstants.FSharpLanguageName, projectFileName, projectFileName, projectGuid, siteProvider, null, errorReporter) 80 | // let project = projectContext :?> AbstractProject 81 | // 82 | // this.SyncProject(project, projectContext, site) 83 | // site.AdviseProjectSiteChanges(FSharpCommonConstants.FSharpLanguageServiceCallbackName, AdviseProjectSiteChanges(fun () -> this.SyncProject(project, projectContext, site))) 84 | // site.AdviseProjectSiteClosed(FSharpCommonConstants.FSharpLanguageServiceCallbackName, AdviseProjectSiteChanges(fun () -> project.Disconnect())) 85 | // | _ -> () 86 | // 87 | // member this.SetupStandAloneFile(fileName: string, fileContents: string, workspace: VisualStudioWorkspaceImpl, hier: IVsHierarchy) = 88 | // let options = FSharpChecker.Instance.GetProjectOptionsFromScript(fileName, fileContents, DateTime.Now, [| |]) |> Async.RunSynchronously 89 | // let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(options.ProjectFileName, options.ProjectFileName) 90 | // 91 | // if not(optionsCache.ContainsKey(projectId)) then 92 | // optionsCache.Add(projectId, options) 93 | // 94 | // if obj.ReferenceEquals(workspace.ProjectTracker.GetProject(projectId), null) then 95 | // let projectContextFactory = this.Package.ComponentModel.GetService(); 96 | // let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) 97 | // 98 | // let projectContext = projectContextFactory.CreateProjectContext(FSharpCommonConstants.FSharpLanguageName, options.ProjectFileName, options.ProjectFileName, projectId.Id, hier, null, errorReporter) 99 | // projectContext.AddSourceFile(fileName) 100 | // 101 | // let project = projectContext :?> AbstractProject 102 | // let document = project.GetCurrentDocumentFromPath(fileName) 103 | // 104 | // document.Closing.Add(fun _ -> project.Disconnect()) 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /ProtoWorkspace/Loaders.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.Loaders 2 | 3 | open Microsoft.CodeAnalysis 4 | open System.Reflection 5 | 6 | type AnalyzerAssemblyLoader() as self = 7 | 8 | member __.AddDependencyLocation(fullPath: string): unit = () 9 | 10 | member __.LoadFromPath(fullPath: string): System.Reflection.Assembly = 11 | Assembly.Load(AssemblyName.GetAssemblyName fullPath) 12 | 13 | interface IAnalyzerAssemblyLoader with 14 | member __.AddDependencyLocation fullPath = self.AddDependencyLocation fullPath 15 | member __.LoadFromPath fullPath = self.LoadFromPath fullPath 16 | 17 | -------------------------------------------------------------------------------- /ProtoWorkspace/MSBuildConfig.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.MSBuildInfo 2 | 3 | open ProtoWorkspace 4 | open System 5 | open System.Collections.Generic 6 | open Microsoft.Build.Framework 7 | open Microsoft.Extensions.Logging 8 | open FSharp.Control 9 | 10 | type MSBuildDiagnosticsMessage = 11 | { LogLevel : string 12 | FileName : string 13 | Text : string 14 | StartLine : int 15 | Endline : int 16 | StartColumn : int 17 | EndColumn : int } 18 | 19 | type MSBuildProjectDiagnostics = 20 | { FileName : string 21 | Warnings : MSBuildDiagnosticsMessage [] 22 | Errors : MSBuildDiagnosticsMessage [] } 23 | 24 | type MSBuildLogForwarder(logger : ILogger, diagnostics : MSBuildDiagnosticsMessage ICollection) as self = 25 | let mutable disposables : ResizeArray = ResizeArray() 26 | 27 | let onError (args : // TODO - Add other/loose configuration properties? 28 | // Properties : string [] 29 | BuildErrorEventArgs) = 30 | logger.LogError args.Message 31 | diagnostics.Add { LogLevel = "Error" 32 | FileName = args.File 33 | Text = args.Message 34 | StartLine = args.LineNumber 35 | Endline = args.ColumnNumber 36 | StartColumn = args.EndLineNumber 37 | EndColumn = args.EndColumnNumber } 38 | 39 | let onWarning (args : BuildWarningEventArgs) = 40 | logger.LogError args.Message 41 | diagnostics.Add { LogLevel = "Warning" 42 | FileName = args.File 43 | Text = args.Message 44 | StartLine = args.LineNumber 45 | Endline = args.ColumnNumber 46 | StartColumn = args.EndLineNumber 47 | EndColumn = args.EndColumnNumber } 48 | 49 | member val Parameters = "" with get, set 50 | member val Verbosity = LoggerVerbosity.Normal with get, set 51 | 52 | member __.Initialize(eventSource : IEventSource) = 53 | eventSource.ErrorRaised.Subscribe onError |> disposables.Add 54 | eventSource.WarningRaised.Subscribe onWarning |> disposables.Add 55 | 56 | member __.Shutdown() = 57 | disposables |> Seq.iter dispose 58 | disposables.Clear() 59 | 60 | interface Microsoft.Build.Framework.ILogger with 61 | member __.Initialize eventSource = self.Initialize eventSource 62 | member __.Shutdown() = self.Shutdown() 63 | 64 | member __.Parameters 65 | with get () = self.Parameters 66 | and set v = self.Parameters <- v 67 | 68 | member __.Verbosity 69 | with get () = self.Verbosity 70 | and set v = self.Verbosity <- v 71 | 72 | type MSBuildOptions = 73 | { ToolsVersion : string 74 | VisualStudioVersion : string 75 | WaitForDebugger : bool 76 | MSBuildExtensionsPath : string } 77 | 78 | type MSBuildProject = 79 | { ProjectGuid : Guid 80 | Path : string 81 | AssemblyName : string 82 | TargetPath : string 83 | TargetFramework : string 84 | SourceFiles : string IList } 85 | -------------------------------------------------------------------------------- /ProtoWorkspace/MSBuildEval.fsx: -------------------------------------------------------------------------------- 1 | System.IO.Directory.SetCurrentDirectory __SOURCE_DIRECTORY__ 2 | #load "scripts/load-references-release.fsx" 3 | 4 | open System.IO 5 | open Microsoft.Build 6 | open Microsoft.Build.Evaluation 7 | open Microsoft.Build.Execution 8 | 9 | 10 | let manager = BuildManager.DefaultBuildManager 11 | 12 | let buildParam = BuildParameters(DetailedSummary=true) 13 | 14 | let fsprojFile = Path.Combine(__SOURCE_DIRECTORY__, "ProtoWorkspace.fsproj") 15 | 16 | File.ReadAllLines fsprojFile 17 | ;; 18 | let project = ProjectInstance fsprojFile 19 | let requestReferences = 20 | BuildRequestData(project, 21 | [| 22 | "ResolveAssemblyReferences" 23 | "ResolveProjectReferences" 24 | |]) 25 | 26 | let fromBuildRes targetName (result:BuildResult) = 27 | result.ResultsByTarget.[targetName].Items 28 | |> Seq.iter(fun r -> printfn "%s" r.ItemSpec) 29 | 30 | let exec() = 31 | let result = manager.Build(buildParam,requestReferences) 32 | fromBuildRes "ResolveProjectReferences" result 33 | fromBuildRes "ResolveAssemblyReferences" result 34 | ;; 35 | exec() 36 | ;; 37 | project.Properties|>Seq.iter(fun p -> printfn "%s -%s" p.Name p.EvaluatedValue) 38 | 39 | 40 | //project.Items|>Seq.iter(fun p -> p.ItemType p.EvaluatedInclude) 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ProtoWorkspace/MSBuildProjectSystem.fs: -------------------------------------------------------------------------------- 1 | module MSBuildProjectSystem 2 | 3 | open Microsoft.Extensions.Configuration 4 | 5 | type IProjectSystem = 6 | abstract Key : string 7 | abstract Language : string 8 | abstract Extensions : string seq 9 | abstract Initialize : Configuration:IConfiguration -> unit 10 | 11 | 12 | 13 | // NOTE - Maybe use the msbuildprojectloader for this? 14 | -------------------------------------------------------------------------------- /ProtoWorkspace/Prelude.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module ProtoWorkspace.Prelude 3 | 4 | open System 5 | open System.IO 6 | open System.Diagnostics 7 | 8 | type FileName = string 9 | type FilePath = string 10 | 11 | let inline debugfn msg = Printf.kprintf Debug.WriteLine msg 12 | let inline failfn msg = Printf.kprintf Debug.Fail msg 13 | 14 | let inline isNull v = 15 | match v with 16 | | null -> true 17 | | _ -> false 18 | 19 | let inline isNotNull v = not (isNull v) 20 | let inline curry f a b = f(a,b) 21 | 22 | let inline dispose (disposable : #IDisposable) = disposable.Dispose() 23 | let inline Ok a = Choice1Of2 a 24 | let inline Fail a = Choice2Of2 a 25 | let inline (|Ok|Fail|) a = a 26 | 27 | /// String Equals Ordinal Ignore Case 28 | let (|EqualsIC|_|) (str : string) arg = 29 | if String.Compare(str, arg, StringComparison.OrdinalIgnoreCase) = 0 then Some() 30 | else None 31 | 32 | 33 | let (|LessThan|_|) a b = if a < b then Some() else None 34 | 35 | let tryCast<'T> (o: obj): 'T option = 36 | match o with 37 | | null -> None 38 | | :? 'T as a -> Some a 39 | | _ -> 40 | debugfn "Cannot cast %O to %O" (o.GetType()) typeof<'T>.Name 41 | None 42 | 43 | /// Null coalescing operator, return non null a, otherwise b 44 | let (?|?) a b = if isNull a then b else a 45 | 46 | let (^) = (<|) 47 | 48 | /// OR predicate combinator 49 | let inline (|?|) (pred1:'a->bool) (pred2:'a->bool) = 50 | fun a -> pred1 a || pred2 a 51 | 52 | /// AND predicate combinator 53 | let inline (|&|) (pred1:'a->bool) (pred2:'a->bool) = 54 | fun a -> pred1 a && pred2 a 55 | 56 | let () path1 path2 = Path.Combine (path1, path2) 57 | 58 | 59 | /// If arg is null raise an `ArgumentNullException` with the argname 60 | let inline checkNullArg arg argName = 61 | if isNull arg then nullArg argName 62 | 63 | 64 | /// Load times used to reset type checking properly on script/project load/unload. It just has to be unique for each project load/reload. 65 | /// Not yet sure if this works for scripts. 66 | let fakeDateTimeRepresentingTimeLoaded x = DateTime(abs (int64 (match x with null -> 0 | _ -> x.GetHashCode())) % 103231L) 67 | 68 | [] 69 | [] 70 | module Array = 71 | let inline private checkNonNull argName arg = 72 | match box arg with 73 | | null -> nullArg argName 74 | | _ -> () 75 | 76 | /// Optimized arrays equality. ~100x faster than `array1 = array2` on strings. 77 | /// ~2x faster for floats 78 | /// ~0.8x slower for ints 79 | let inline areEqual (xs : 'T []) (ys : 'T []) = 80 | match xs, ys with 81 | | null, null -> true 82 | | [||], [||] -> true 83 | | null, _ | _, null -> false 84 | | _ when xs.Length <> ys.Length -> false 85 | | _ -> 86 | let mutable break' = false 87 | let mutable i = 0 88 | let mutable result = true 89 | while i < xs.Length && not break' do 90 | if xs.[i] <> ys.[i] then 91 | break' <- true 92 | result <- false 93 | i <- i + 1 94 | result 95 | 96 | /// check if subArray is found in the wholeArray starting 97 | /// at the provided index 98 | let inline isSubArray (subArray : 'T []) (wholeArray : 'T []) index = 99 | if isNull subArray || isNull wholeArray then false 100 | elif subArray.Length = 0 then true 101 | elif subArray.Length > wholeArray.Length then false 102 | elif subArray.Length = wholeArray.Length then areEqual subArray wholeArray 103 | else 104 | let rec loop subidx idx = 105 | if subidx = subArray.Length then true 106 | elif subArray.[subidx] = wholeArray.[idx] then loop (subidx + 1) (idx + 1) 107 | else false 108 | loop 0 index 109 | 110 | /// Returns true if one array has another as its subset from index 0. 111 | let startsWith (prefix : _ []) (whole : _ []) = isSubArray prefix whole 0 112 | 113 | /// Returns true if one array has trailing elements equal to another's. 114 | let endsWith (suffix : _ []) (whole : _ []) = isSubArray suffix whole (whole.Length - suffix.Length) 115 | 116 | /// Returns a new array with an element replaced with a given value. 117 | let replace index value (array : _ []) = 118 | checkNonNull "array" array 119 | if index >= array.Length then raise (IndexOutOfRangeException "index") 120 | let res = Array.copy array 121 | res.[index] <- value 122 | res 123 | 124 | /// Returns all heads of a given array. 125 | /// For [|1;2;3|] it returns [|[|1; 2; 3|]; [|1; 2|]; [|1|]|] 126 | let heads (array : 'T []) = 127 | checkNonNull "array" array 128 | let res = Array.zeroCreate<'T []> array.Length 129 | for i = array.Length - 1 downto 0 do 130 | res.[i] <- array.[0..i] 131 | res 132 | 133 | /// Fold over the array passing the index and element at that index to a folding function 134 | let foldi (folder : 'State -> int -> 'T -> 'State) (state : 'State) (array : 'T []) = 135 | checkNonNull "array" array 136 | if array.Length = 0 then state 137 | else 138 | let folder = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder 139 | let mutable state : 'State = state 140 | let len = array.Length 141 | for i = 0 to len - 1 do 142 | state <- folder.Invoke(state, i, array.[i]) 143 | state 144 | 145 | /// pass an array byref to reverse it in place 146 | let revInPlace (array : 'T []) = 147 | checkNonNull "array" array 148 | if areEqual array [||] then () 149 | else 150 | let arrlen, revlen = array.Length - 1, array.Length / 2 - 1 151 | for idx in 0..revlen do 152 | let t1 = array.[idx] 153 | let t2 = array.[arrlen - idx] 154 | array.[idx] <- t2 155 | array.[arrlen - idx] <- t1 156 | 157 | /// Map all elements of the array that satisfy the predicate 158 | let filterMap predicate mapfn (array : 'T []) = 159 | checkNonNull "array" array 160 | if array.Length = 0 then [||] 161 | else 162 | let result = Array.zeroCreate array.Length 163 | let mutable count = 0 164 | for elm in array do 165 | if predicate elm then 166 | result.[count] <- mapfn elm 167 | count <- count + 1 168 | if count = 0 then [||] 169 | else result.[0..count - 1] 170 | 171 | [] 172 | module Seq = 173 | let sortWithDescending fn source = 174 | source |> Seq.sortWith (fun a b -> fn b a) 175 | 176 | [] 177 | [] 178 | module String = 179 | let inline toCharArray (str : string) = str.ToCharArray() 180 | 181 | let lowerCaseFirstChar (str : string) = 182 | if String.IsNullOrEmpty str || Char.IsLower(str, 0) then str 183 | else 184 | let strArr = toCharArray str 185 | match Array.tryHead strArr with 186 | | None -> str 187 | | Some c -> 188 | strArr.[0] <- Char.ToLower c 189 | String strArr 190 | 191 | let inline contains (target : string) (str : string) = str.Contains target 192 | let inline equalsIgnoreCase (str1 : string) (str2 : string) = str1.Equals(str2, StringComparison.OrdinalIgnoreCase) 193 | 194 | let extractTrailingIndex (str : string) = 195 | match str with 196 | | null -> null, None 197 | | _ -> 198 | let charr = str.ToCharArray() 199 | Array.revInPlace charr 200 | let digits = Array.takeWhile Char.IsDigit charr 201 | Array.revInPlace digits 202 | String digits |> function 203 | | "" -> str, None 204 | | index -> str.Substring(0, str.Length - index.Length), Some(int index) 205 | 206 | /// Remove all trailing and leading whitespace from the string 207 | /// return null if the string is null 208 | let trim (value : string) = 209 | if isNull value then null 210 | else value.Trim() 211 | 212 | /// Splits a string into substrings based on the strings in the array separators 213 | let split options (separator : string []) (value : string) = 214 | if isNull value then null 215 | else value.Split(separator, options) 216 | 217 | let (|StartsWith|_|) pattern value = 218 | if String.IsNullOrWhiteSpace value then None 219 | elif value.StartsWith pattern then Some() 220 | else None 221 | 222 | let (|Contains|_|) pattern value = 223 | if String.IsNullOrWhiteSpace value then None 224 | elif value.Contains pattern then Some() 225 | else None 226 | 227 | open System.IO 228 | 229 | let getLines (str : string) = 230 | use reader = new StringReader(str) 231 | [| let line = ref (reader.ReadLine()) 232 | while isNotNull (!line) do 233 | yield !line 234 | line := reader.ReadLine() 235 | if str.EndsWith "\n" then 236 | // last trailing space not returned 237 | // http://stackoverflow.com/questions/19365404/stringreader-omits-trailing-linebreak 238 | yield String.Empty |] 239 | 240 | let getNonEmptyLines (str : string) = 241 | use reader = new StringReader(str) 242 | [| let line = ref (reader.ReadLine()) 243 | while isNotNull (!line) do 244 | if (!line).Length > 0 then yield !line 245 | line := reader.ReadLine() |] 246 | 247 | /// match strings with ordinal ignore case 248 | let inline equalsIC (str1:string) (str2:string) = str1.Equals(str2,StringComparison.OrdinalIgnoreCase) 249 | 250 | /// Parse a string to find the first nonempty line 251 | /// Return null if the string was null or only contained empty lines 252 | let firstNonEmptyLine (str : string) = 253 | use reader = new StringReader(str) 254 | 255 | let rec loop (line : string) = 256 | if isNull line then None 257 | elif line.Length > 0 then Some line 258 | else loop (reader.ReadLine()) 259 | loop (reader.ReadLine()) 260 | 261 | open System.Text 262 | 263 | [] 264 | module StringBuilder = 265 | /// Pipelining function for appending a string to a stringbuilder 266 | let inline append (str : string) (sb : StringBuilder) = sb.Append str 267 | 268 | /// Pipelining function for appending a string with a '\n' to a stringbuilder 269 | let inline appendLine (str : string) (sb : StringBuilder) = sb.AppendLine str 270 | 271 | /// SideEffecting function for appending a string to a stringbuilder 272 | let inline appendi (str : string) (sb : StringBuilder) = sb.Append str |> ignore 273 | 274 | /// SideEffecting function for appending a string with a '\n' to a stringbuilder 275 | let inline appendLinei (str : string) (sb : StringBuilder) = sb.AppendLine str |> ignore 276 | 277 | [] 278 | module Dict = 279 | open System.Collections.Generic 280 | 281 | let add key value (dict : #IDictionary<_, _>) = 282 | dict.[key] <- value 283 | dict 284 | 285 | let remove (key : 'k) (dict : #IDictionary<'k, _>) = 286 | dict.Remove key |> ignore 287 | dict 288 | 289 | let tryFind key (dict : #IDictionary<'k, 'v>) = 290 | let mutable value = Unchecked.defaultof<_> 291 | if dict.TryGetValue(key, &value) then Some value 292 | else None 293 | 294 | let ofSeq (xs : ('k * 'v) seq) = 295 | let dict = Dictionary() 296 | for k, v in xs do 297 | dict.[k] <- v 298 | dict 299 | 300 | module PropertyConverter = 301 | // TODO - railway this 302 | let toGuid propertyValue = 303 | match Guid.TryParse propertyValue with 304 | | true, value -> Some value 305 | | _ -> None 306 | 307 | let toDefineConstants propertyValue = 308 | if String.IsNullOrWhiteSpace propertyValue then [||] 309 | else propertyValue.Split([| ';' |], StringSplitOptions.RemoveEmptyEntries) 310 | 311 | // TODO - railway this 312 | let toBoolean propertyValue = 313 | if propertyValue = String.Empty then false else 314 | match Boolean.TryParse propertyValue with 315 | | true, value -> value 316 | | _ -> failwithf "Couldn't parse '%s' into a Boolean" propertyValue 317 | 318 | let toBooleanOr propertyValue defaultArg = 319 | match Boolean.TryParse propertyValue with 320 | | true, value -> value 321 | | _ -> defaultArg 322 | (* 323 | Omnisharp does it like this 324 | 325 | public static bool? ToBoolean(string propertyValue) 326 | { 327 | if (string.IsNullOrWhiteSpace(propertyValue)) 328 | { 329 | return null; 330 | } 331 | 332 | try 333 | { 334 | return Convert.ToBoolean(propertyValue); 335 | } 336 | catch (FormatException) 337 | { 338 | return null; 339 | } 340 | } 341 | 342 | public static bool ToBoolean(string propertyValue, bool defaultValue) 343 | { 344 | if (string.IsNullOrWhiteSpace(propertyValue)) 345 | { 346 | return defaultValue; 347 | } 348 | 349 | try 350 | { 351 | return Convert.ToBoolean(propertyValue); 352 | } 353 | catch (FormatException) 354 | { 355 | return defaultValue; 356 | } 357 | } 358 | *) 359 | 360 | 361 | 362 | 363 | type internal Assert() = 364 | /// Display a good exception for this error message and then rethrow. 365 | static member Exception(e:Exception) = 366 | System.Diagnostics.Debug.Assert(false, "Unexpected exception seen in language service", e.ToString()) 367 | 368 | 369 | [] 370 | [] 371 | module Option = 372 | let inline ofNull value = 373 | if obj.ReferenceEquals(value, null) then None else Some value 374 | 375 | let inline ofNullable (value: Nullable<'T>) = 376 | if value.HasValue then Some value.Value else None 377 | 378 | let inline toNullable (value: 'T option) = 379 | match value with 380 | | Some x -> Nullable<_> x 381 | | None -> Nullable<_> () 382 | 383 | let inline attempt (f: unit -> 'T) = try Some <| f() with _ -> None 384 | 385 | /// Gets the value associated with the option or the supplied default value. 386 | let inline getOrElse v = 387 | function 388 | | Some x -> x 389 | | None -> v 390 | 391 | /// Gets the option if Some x, otherwise the supplied default value. 392 | let inline orElse v = 393 | function 394 | | Some x -> Some x 395 | | None -> v 396 | 397 | /// Gets the value if Some x, otherwise try to get another value by calling a function 398 | let inline getOrTry f = 399 | function 400 | | Some x -> x 401 | | None -> f() 402 | 403 | /// Gets the option if Some x, otherwise try to get another value 404 | let inline orTry f = 405 | function 406 | | Some x -> Some x 407 | | None -> f() 408 | 409 | /// Some(Some x) -> Some x | None -> None 410 | let inline flatten x = 411 | match x with 412 | | Some x -> x 413 | | None -> None 414 | 415 | let inline toList x = 416 | match x with 417 | | Some x -> [x] 418 | | None -> [] 419 | 420 | let inline iterElse someAction noneAction opt = 421 | match opt with 422 | | Some x -> someAction x 423 | | None -> noneAction () 424 | 425 | // Async helper functions copied from https://github.com/jack-pappas/ExtCore/blob/master/ExtCore/ControlCollections.Async.fs 426 | [] 427 | [] 428 | module Async = 429 | /// Transforms an Async value using the specified function. 430 | [] 431 | let map (mapping : 'T -> 'U) (value : Async<'T>) : Async<'U> = 432 | async { 433 | // Get the input value. 434 | let! x = value 435 | // Apply the mapping function and return the result. 436 | return mapping x 437 | } 438 | 439 | // Transforms an Async value using the specified Async function. 440 | [] 441 | let bind (binding : 'T -> Async<'U>) (value : Async<'T>) : Async<'U> = 442 | async { 443 | // Get the input value. 444 | let! x = value 445 | // Apply the binding function and return the result. 446 | return! binding x 447 | } 448 | 449 | [] 450 | module Array = 451 | /// Async implementation of Array.map. 452 | let map (mapping : 'T -> Async<'U>) (array : 'T[]) : Async<'U[]> = 453 | let len = Array.length array 454 | let result = Array.zeroCreate len 455 | 456 | async { // Apply the mapping function to each array element. 457 | for i in 0 .. len - 1 do 458 | let! mappedValue = mapping array.[i] 459 | result.[i] <- mappedValue 460 | 461 | // Return the completed results. 462 | return result 463 | } 464 | 465 | /// Async implementation of Array.mapi. 466 | let mapi (mapping : int -> 'T -> Async<'U>) (array : 'T[]) : Async<'U[]> = 467 | let len = Array.length array 468 | let result = Array.zeroCreate len 469 | 470 | async { 471 | // Apply the mapping function to each array element. 472 | for i in 0 .. len - 1 do 473 | let! mappedValue = mapping i array.[i] 474 | result.[i] <- mappedValue 475 | 476 | // Return the completed results. 477 | return result 478 | } 479 | 480 | /// Async implementation of Array.exists. 481 | let exists (predicate : 'T -> Async) (array : 'T[]) : Async = 482 | let len = Array.length array 483 | let rec loop i = 484 | async { 485 | if i >= len then 486 | return false 487 | else 488 | let! found = predicate array.[i] 489 | if found then 490 | return true 491 | else 492 | return! loop (i + 1) 493 | } 494 | loop 0 495 | 496 | [] 497 | module List = 498 | let rec private mapImpl (mapping, mapped : 'U list, pending : 'T list) = 499 | async { 500 | match pending with 501 | | [] -> 502 | // Reverse the list of mapped values before returning it. 503 | return List.rev mapped 504 | 505 | | el :: pending -> 506 | // Apply the current list element to the mapping function. 507 | let! mappedEl = mapping el 508 | 509 | // Cons the result to the list of mapped values, then continue 510 | // mapping the rest of the pending list elements. 511 | return! mapImpl (mapping, mappedEl :: mapped, pending) 512 | } 513 | 514 | /// Async implementation of List.map. 515 | let map (mapping : 'T -> Async<'U>) (list : 'T list) : Async<'U list> = 516 | mapImpl (mapping, [], list) 517 | 518 | 519 | 520 | /// Maybe computation expression builder, copied from ExtCore library 521 | /// https://github.com/jack-pappas/ExtCore/blob/master/ExtCore/Control.fs 522 | [] 523 | type MaybeBuilder () = 524 | // 'T -> M<'T> 525 | [] 526 | member inline __.Return value: 'T option = Some value 527 | 528 | // M<'T> -> M<'T> 529 | [] 530 | member inline __.ReturnFrom value: 'T option = value 531 | 532 | // unit -> M<'T> 533 | [] 534 | member inline __.Zero (): unit option = Some () // TODO: Should this be None? 535 | 536 | // (unit -> M<'T>) -> M<'T> 537 | [] 538 | member __.Delay (f: unit -> 'T option): 'T option = f () 539 | 540 | // M<'T> -> M<'T> -> M<'T> 541 | // or 542 | // M -> M<'T> -> M<'T> 543 | [] 544 | member inline __.Combine (r1, r2: 'T option): 'T option = 545 | match r1 with 546 | | None -> None 547 | | Some () -> r2 548 | 549 | // M<'T> * ('T -> M<'U>) -> M<'U> 550 | [] 551 | member inline __.Bind (value, f: 'T -> 'U option): 'U option = Option.bind f value 552 | 553 | // 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable 554 | [] 555 | member __.Using (resource: ('T :> System.IDisposable), body: _ -> _ option): _ option = 556 | try body resource 557 | finally 558 | if not <| obj.ReferenceEquals (null, box resource) then 559 | resource.Dispose () 560 | 561 | // (unit -> bool) * M<'T> -> M<'T> 562 | [] 563 | member x.While (guard, body: _ option): _ option = 564 | if guard () then 565 | // OPTIMIZE: This could be simplified so we don't need to make calls to Bind and While. 566 | x.Bind (body, (fun () -> x.While (guard, body))) 567 | else 568 | x.Zero () 569 | 570 | // seq<'T> * ('T -> M<'U>) -> M<'U> 571 | // or 572 | // seq<'T> * ('T -> M<'U>) -> seq> 573 | [] 574 | member x.For (sequence: seq<_>, body: 'T -> unit option): _ option = 575 | // OPTIMIZE: This could be simplified so we don't need to make calls to Using, While, Delay. 576 | using (sequence.GetEnumerator()) (fun enum -> 577 | x.While (enum.MoveNext, 578 | x.Delay (fun () -> body enum.Current))) 579 | 580 | 581 | 582 | [] 583 | type AsyncMaybeBuilder () = 584 | [] 585 | member __.Return value : Async<'T option> = Some value |> async.Return 586 | 587 | [] 588 | member __.ReturnFrom value : Async<'T option> = value 589 | 590 | [] 591 | member __.ReturnFrom (value: 'T option) : Async<'T option> = async.Return value 592 | 593 | [] 594 | member __.Zero () : Async = Some () |> async.Return 595 | 596 | [] 597 | member __.Delay (f : unit -> Async<'T option>) : Async<'T option> = f () 598 | 599 | [] 600 | member __.Combine (r1, r2 : Async<'T option>) : Async<'T option> = 601 | async { 602 | let! r1' = r1 603 | match r1' with 604 | | None -> return None 605 | | Some () -> return! r2 606 | } 607 | 608 | [] 609 | member __.Bind (value: Async<'T option>, f : 'T -> Async<'U option>) : Async<'U option> = 610 | async { 611 | let! value' = value 612 | match value' with 613 | | None -> return None 614 | | Some result -> return! f result 615 | } 616 | 617 | [] 618 | member __.Bind (value: 'T option, f : 'T -> Async<'U option>) : Async<'U option> = 619 | async { 620 | match value with 621 | | None -> return None 622 | | Some result -> return! f result 623 | } 624 | 625 | [] 626 | member __.Using (resource : ('T :> IDisposable), body : _ -> Async<_ option>) : Async<_ option> = 627 | try body resource 628 | finally 629 | if isNotNull resource then resource.Dispose () 630 | 631 | [] 632 | member x.While (guard, body : Async<_ option>) : Async<_ option> = 633 | if guard () then 634 | x.Bind (body, (fun () -> x.While (guard, body))) 635 | else 636 | x.Zero () 637 | 638 | [] 639 | member x.For (sequence : seq<_>, body : 'T -> Async) : Async<_ option> = 640 | x.Using (sequence.GetEnumerator (), fun enum -> 641 | x.While (enum.MoveNext, x.Delay (fun () -> body enum.Current))) 642 | 643 | [] 644 | member inline __.TryWith (computation : Async<'T option>, catchHandler : exn -> Async<'T option>) : Async<'T option> = 645 | async.TryWith (computation, catchHandler) 646 | 647 | [] 648 | member inline __.TryFinally (computation : Async<'T option>, compensation : unit -> unit) : Async<'T option> = 649 | async.TryFinally (computation, compensation) 650 | 651 | [] 652 | module AsyncMaybe = 653 | let inline liftAsync (async : Async<'T>) : Async<_ option> = 654 | async |> Async.map Some 655 | 656 | 657 | let maybe = MaybeBuilder() 658 | let asyncMaybe = AsyncMaybeBuilder() 659 | 660 | 661 | [] 662 | module Directory = 663 | 664 | let fromPath path = (FileInfo path).Directory.FullName -------------------------------------------------------------------------------- /ProtoWorkspace/ProjectConfig.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | open System.IO 4 | 5 | type ProjectSettings = 6 | { IsForStandaloneScript : bool 7 | ProjectFile : FileName 8 | TargetFramework : FSharpTargetFramework 9 | CompilerVersion : FSharpCompilerVersion option 10 | CompilerOptions : string [] 11 | SourceFiles : FileName [] 12 | FullOutputFilePath : FileName option 13 | References : FileName [] 14 | ProjectReferences : FileName [] } 15 | 16 | type ProjectConfig = 17 | | FsProject of ProjectSettings 18 | | FsxProject of ProjectSettings 19 | | SigProject of ProjectSettings 20 | 21 | [] 22 | module ProjectConfig = 23 | /// F# project file extension - `.fsproj` 24 | [] 25 | let fsprojext = ".fsproj" 26 | 27 | (* Compiler Flags *) 28 | 29 | /// Compiler Flag `--noframework` 30 | [] 31 | let noframeworkFlag = "--noframework" 32 | 33 | /// Compiler Flag `--debug-` 34 | [] 35 | let debugFlag = "--debug-" 36 | 37 | /// Compiler Flag `--optimize-` 38 | [] 39 | let optimizeFlag = "--optimize-" 40 | 41 | /// Compiler Flag `--tailcalls-` 42 | [] 43 | let tailcallsFlag = "--tailcalls-" 44 | 45 | /// Checks a file path to see if the extension matches `.fsproj` 46 | let isFSharpProject projectPath = String.equalsIgnoreCase (Path.GetExtension projectPath) fsprojext 47 | 48 | /// Creates a ProjectConfig for a normal F# Project 49 | let fsProjectConfig (projectPath, targetFramework, fscVersion, fscOptions, srcFiles, outputPath, references, 50 | projectReferences) = 51 | { IsForStandaloneScript = false 52 | ProjectFile = projectPath 53 | TargetFramework = targetFramework 54 | CompilerVersion = fscVersion 55 | CompilerOptions = fscOptions 56 | SourceFiles = srcFiles 57 | FullOutputFilePath = outputPath 58 | References = references 59 | ProjectReferences = projectReferences } 60 | |> FsProject 61 | 62 | /// Creates an ad-hoc ProjectConfig to integrate generated signatures into the project system 63 | let signatureProjectConfig (sigpath : FileName) (project : ProjectSettings) = 64 | let sigProjectName = sigpath + fsprojext 65 | let sourceFiles = [| sigpath |] 66 | let flags = [| noframeworkFlag; debugFlag; optimizeFlag; tailcallsFlag |] 67 | { IsForStandaloneScript = true 68 | ProjectFile = sigProjectName 69 | TargetFramework = project.TargetFramework 70 | CompilerVersion = project.CompilerVersion 71 | CompilerOptions = flags 72 | SourceFiles = sourceFiles 73 | FullOutputFilePath = Some(Path.ChangeExtension(sigProjectName, ".dll")) 74 | References = [||] 75 | ProjectReferences = [||] } 76 | |> SigProject 77 | 78 | /// Creates a standalone ProjectConfig for an fsx script file 79 | let fsxProjectConfig (fsxPath : FileName) (compilerVersion) = 80 | let fsxProjectName = fsxPath + fsprojext 81 | let flags = [| noframeworkFlag; debugFlag; optimizeFlag; tailcallsFlag |] 82 | { IsForStandaloneScript = true 83 | ProjectFile = fsxProjectName 84 | TargetFramework = FSharpTargetFramework.NET_4_5 85 | CompilerVersion = Some compilerVersion 86 | CompilerOptions = flags 87 | SourceFiles = [| fsxPath |] 88 | FullOutputFilePath = Some(Path.ChangeExtension(fsxProjectName, ".dll")) 89 | References = [||] 90 | ProjectReferences = [||] } 91 | |> FsxProject 92 | -------------------------------------------------------------------------------- /ProtoWorkspace/ProjectFileInfo.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | open System 4 | open System.IO 5 | open Microsoft.CodeAnalysis 6 | open System.Runtime.Versioning 7 | open Microsoft.Build.Evaluation 8 | open Microsoft.Build.Execution 9 | 10 | 11 | /// Specifies the version of the F# compiler that should be used 12 | type LanguageVersion = 13 | | FSharp2 14 | | FSharp3 15 | | FSharp4 16 | 17 | type PlatformType = 18 | | X86 19 | | X64 20 | | AnyCPU 21 | 22 | override self.ToString() = 23 | self |> function 24 | | X86 -> Constants.X86 25 | | X64 -> Constants.X64 26 | | AnyCPU -> Constants.AnyCPU 27 | 28 | static member Parse text = 29 | text |> function 30 | | EqualsIC Constants.X86 -> X86 31 | | EqualsIC Constants.X64 -> X64 32 | | EqualsIC "Any CPU" | EqualsIC Constants.AnyCPU -> AnyCPU 33 | | _ -> failwithf "Could not parse '%s' into a `PlatformType`" text 34 | 35 | static member TryParse text = 36 | text |> function 37 | | EqualsIC Constants.X86 -> Some X86 38 | | EqualsIC Constants.X64 -> Some X64 39 | | EqualsIC "Any CPU" | EqualsIC Constants.AnyCPU -> Some AnyCPU 40 | | _ -> Option.None 41 | 42 | /// Determines the output of compiling the F# Project 43 | type OutputType = 44 | /// An .exe with an entry point and a console. 45 | | Exe 46 | /// An .exe with an entry point but no console. 47 | | Winexe 48 | /// a dynamically linked library (.dll) 49 | | Library 50 | /// Build a module that can be added to another assembly (.netmodule) 51 | | Module 52 | 53 | override self.ToString() = self |> function 54 | | Exe -> Constants.Exe 55 | | Winexe -> Constants.Winexe 56 | | Library -> Constants.Library 57 | | Module -> Constants.Module 58 | 59 | static member Parse text = text |> function 60 | | EqualsIC Constants.Exe -> Exe 61 | | EqualsIC Constants.Winexe -> Winexe 62 | | EqualsIC Constants.Library -> Library 63 | | EqualsIC Constants.Module -> Module 64 | | _ -> failwithf "Could not parse '%s' into a `OutputType`" text 65 | 66 | static member TryParse text = text |> function 67 | | EqualsIC Constants.Exe -> Some Exe 68 | | EqualsIC Constants.Winexe -> Some Winexe 69 | | EqualsIC Constants.Library -> Some Library 70 | | EqualsIC Constants.Module -> Some Module 71 | | _ -> None 72 | 73 | // TODO - add another field to store `AdditionalDocuments` for use during ProjectInfo creation 74 | /// ProjectFileInfo combines the information needed to create a ProjectInfo for the workspace 75 | /// and the configuration info to create a FSharpProjectOptions 76 | type ProjectFileInfo = { 77 | ProjectId : ProjectId 78 | ProjectGuid : Guid option 79 | Name : string option 80 | ProjectFilePath : string 81 | TargetFramework : FrameworkName option 82 | FrameworkVersion : string option 83 | AssemblyName : string 84 | OutputPath : string 85 | OutputType : OutputType 86 | SignAssembly : bool 87 | AssemblyOriginatorKeyFile : string option 88 | GenerateXmlDocumentation : string option 89 | Options : string [] 90 | PreprocessorSymbolNames : string [] 91 | CompileFiles : string [] 92 | ResourceFiles : string [] 93 | EmbeddedResourceFiles : string [] 94 | ContentFiles : string [] 95 | PageFiles : string [] 96 | ScriptFiles : string [] 97 | OtherFiles : string [] 98 | References : string [] 99 | /// Collection of paths fsproj files for the project references 100 | ProjectReferences : string [] 101 | Analyzers : string [] 102 | // LogOutput : string 103 | } with 104 | member self.ProjectDirectory = Path.GetDirectoryName self.ProjectFilePath 105 | 106 | 107 | [] 108 | module ProjectFileInfo = 109 | open Microsoft.CodeAnalysis.Diagnostics 110 | open ProtoWorkspace.Loaders 111 | 112 | let getFullPath (projectItem : ProjectItemInstance) = projectItem.GetMetadataValue MetadataName.FullPath 113 | let isProjectReference (projectItem : ProjectItemInstance) : bool = 114 | projectItem.GetMetadataValue(MetadataName.ReferenceSourceTarget) 115 | .Equals(ItemName.ProjectReference, StringComparison.OrdinalIgnoreCase) 116 | 117 | 118 | let create (projectFilePath:string) = 119 | if not (File.Exists projectFilePath) then failwithf "No project file found at '%s'" projectFilePath else 120 | 121 | let manager = BuildManager.DefaultBuildManager 122 | 123 | let buildParam = BuildParameters(DetailedSummary=true) 124 | let project = Project projectFilePath 125 | let projectInstance = project.CreateProjectInstance() 126 | 127 | 128 | let requestReferences = 129 | BuildRequestData (projectInstance, 130 | [| "ResolveAssemblyReferences" 131 | "ResolveProjectReferences" 132 | "ResolveReferenceDependencies" 133 | |]) 134 | 135 | let result = manager.Build(buildParam,requestReferences) 136 | 137 | let fromBuildRes targetName = 138 | if result.ResultsByTarget.ContainsKey targetName then 139 | result.ResultsByTarget.[targetName].Items 140 | |> Seq.map(fun r -> r.ItemSpec) 141 | |> Array.ofSeq 142 | else 143 | [||] 144 | 145 | let projectReferences = fromBuildRes "ResolveProjectReferences" 146 | 147 | let references = fromBuildRes "ResolveAssemblyReferences" 148 | 149 | let getItems itemType = 150 | if project.ItemTypes.Contains itemType then 151 | project.GetItems itemType 152 | |> Seq.map(fun item -> item.EvaluatedInclude) 153 | else 154 | Seq.empty 155 | 156 | let getProperty propName = 157 | let s = project.GetPropertyValue propName 158 | if String.IsNullOrWhiteSpace s then None 159 | else Some s 160 | 161 | let outFileOpt = getProperty "TargetPath" 162 | 163 | let getbool (s:string option) = 164 | match s with 165 | | None -> false 166 | | Some s -> 167 | match Boolean.TryParse s with 168 | | true, result -> result | false, _ -> false 169 | 170 | let split (s:string option) (cs:char[]) = 171 | match s with 172 | | None -> [||] 173 | | Some s -> 174 | if String.IsNullOrWhiteSpace s then [||] 175 | else s.Split(cs, StringSplitOptions.RemoveEmptyEntries) 176 | 177 | let fxVer = getProperty "TargetFrameworkVersion" 178 | let optimize = getProperty "Optimize" |> getbool 179 | let assemblyNameOpt = getProperty "AssemblyName" 180 | let tailcalls = getProperty "Tailcalls" |> getbool 181 | let outputPathOpt = getProperty "OutputPath" 182 | let docFileOpt = getProperty "DocumentationFile" 183 | let outputTypeOpt = getProperty "OutputType" 184 | let debugTypeOpt = getProperty "DebugType" 185 | let baseAddressOpt = getProperty "BaseAddress" 186 | let sigFileOpt = getProperty "GenerateSignatureFile" 187 | let keyFileOpt = getProperty "KeyFile" 188 | let pdbFileOpt = getProperty "PdbFile" 189 | let platformOpt = getProperty "Platform" 190 | let targetTypeOpt = getProperty "TargetType" 191 | let versionFileOpt = getProperty "VersionFile" 192 | let targetProfileOpt = getProperty "TargetProfile" 193 | let warnLevelOpt = getProperty "Warn" 194 | let subsystemVersionOpt = getProperty "SubsystemVersion" 195 | let win32ResOpt = getProperty "Win32ResourceFile" 196 | let heOpt = getProperty "HighEntropyVA" |> getbool 197 | let win32ManifestOpt = getProperty "Win32ManifestFile" 198 | let debugSymbols = getProperty "DebugSymbols" |> getbool 199 | let prefer32bit = getProperty "Prefer32Bit" |> getbool 200 | let warnAsError = getProperty "TreatWarningsAsErrors" |> getbool 201 | let defines = split (getProperty "DefineConstants") [| ';'; ','; ' ' |] 202 | let nowarn = split (getProperty "NoWarn") [| ';'; ','; ' ' |] 203 | let warningsAsError = split (getProperty "WarningsAsErrors") [| ';'; ','; ' ' |] 204 | let libPaths = split (getProperty "ReferencePath") [| ';'; ',' |] 205 | let otherFlags = split (getProperty "OtherFlags") [| ' ' |] 206 | let isLib = 207 | match outputTypeOpt with 208 | | None -> false 209 | | Some prop -> prop ="Library" 210 | 211 | let pages = getItems "Page" 212 | let embeddedResources = getItems "EmbeddedResource" 213 | let files = getItems "Compile" 214 | let resources = getItems "Resource" 215 | let noaction = getItems "None" 216 | let content = getItems "Content" 217 | 218 | 219 | let fscFlag str (opt:string option) = seq{ 220 | match opt with 221 | | None -> () 222 | | Some s -> yield str + s 223 | } 224 | 225 | let fscFlags flag (ls:string []) = seq { 226 | for x in ls do 227 | if not (String.IsNullOrWhiteSpace x) then yield flag + x 228 | } 229 | 230 | let options = [ 231 | yield "--simpleresolution" 232 | yield "--noframework" 233 | yield! fscFlag "--out:" outFileOpt 234 | yield! fscFlag "--doc:" docFileOpt 235 | yield! fscFlag "--baseaddress:" baseAddressOpt 236 | yield! fscFlag "--keyfile:" keyFileOpt 237 | yield! fscFlag "--sig:" sigFileOpt 238 | yield! fscFlag "--pdb:" pdbFileOpt 239 | yield! fscFlag "--versionfile:" versionFileOpt 240 | yield! fscFlag "--warn:" warnLevelOpt 241 | yield! fscFlag "--subsystemversion:" subsystemVersionOpt 242 | if heOpt then yield "--highentropyva+" 243 | yield! fscFlag "--win32res:" win32ResOpt 244 | yield! fscFlag "--win32manifest:" win32ManifestOpt 245 | yield! fscFlag "--targetprofile:" targetProfileOpt 246 | yield "--fullpaths" 247 | yield "--flaterrors" 248 | if warnAsError then yield "--warnaserror" 249 | yield 250 | if isLib then "--target:library" 251 | else "--target:exe" 252 | yield! fscFlags "--define:" defines 253 | yield! fscFlags "--nowarn:" nowarn 254 | yield! fscFlags "--warnaserror:" warningsAsError 255 | yield if debugSymbols then "--debug+" else "--debug-" 256 | yield if optimize then "--optimize+" else "--optimize-" 257 | yield if tailcalls then "--tailcalls+" else "--tailcalls-" 258 | match debugTypeOpt with 259 | | None -> () 260 | | Some debugType -> 261 | match debugType.ToUpperInvariant() with 262 | | "NONE" -> () 263 | | "PDBONLY" -> yield "--debug:pdbonly" 264 | | "FULL" -> yield "--debug:full" 265 | | _ -> () 266 | match platformOpt |> Option.map (fun o -> o.ToUpperInvariant()), prefer32bit, 267 | targetTypeOpt |> Option.map (fun o -> o.ToUpperInvariant()) with 268 | | Some "ANYCPU", true, Some "EXE" | Some "ANYCPU", true, Some "WINEXE" -> yield "--platform:anycpu32bitpreferred" 269 | | Some "ANYCPU", _, _ -> yield "--platform:anycpu" 270 | | Some "X86", _, _ -> yield "--platform:x86" 271 | | Some "X64", _, _ -> yield "--platform:x64" 272 | | Some "ITANIUM", _, _ -> yield "--platform:Itanium" 273 | | _ -> () 274 | match targetTypeOpt |> Option.map (fun o -> o.ToUpperInvariant()) with 275 | | Some "LIBRARY" -> yield "--target:library" 276 | | Some "EXE" -> yield "--target:exe" 277 | | Some "WINEXE" -> yield "--target:winexe" 278 | | Some "MODULE" -> yield "--target:module" 279 | | _ -> () 280 | yield! otherFlags 281 | yield! Seq.map((+)"--resource:") resources 282 | yield! Seq.map((+)"--lib:") libPaths 283 | yield! Seq.map((+)"--r:") references 284 | yield! files 285 | ] 286 | 287 | let getItemPaths itemName = 288 | projectInstance.GetItems itemName |> Seq.map getFullPath 289 | 290 | let filterItemPaths predicate itemName = 291 | projectInstance.GetItems itemName 292 | |> Seq.filter predicate 293 | |> Seq.map getFullPath 294 | 295 | let isScriptFile path = 296 | String.equalsIC (path |> Path.GetExtension) ".fsx" 297 | 298 | let sourceFiles = getItemPaths ItemName.Compile 299 | 300 | let otherFiles = 301 | filterItemPaths (fun x -> not ^ isScriptFile x.EvaluatedInclude) ItemName.None 302 | 303 | let scriptFiles = 304 | filterItemPaths (fun x -> isScriptFile x.EvaluatedInclude) ItemName.None 305 | 306 | let references = 307 | projectInstance.GetItems ItemName.ReferencePath 308 | |> Seq.filter (not< Seq.map getFullPath 310 | 311 | let projectReferences = 312 | projectInstance.GetItems ItemName.ProjectReference 313 | |> Seq.filter isProjectReference 314 | |> Seq.map getFullPath 315 | 316 | let analyzers = getItemPaths ItemName.Analyzer 317 | 318 | let projectGuid = 319 | projectInstance.TryGetPropertyValue Property.ProjectGuid 320 | |> Option.bind PropertyConverter.toGuid 321 | 322 | let projectId = 323 | defaultArg (projectGuid |> Option.map ^ fun x -> ProjectId.CreateFromSerialized x) 324 | (ProjectId.CreateNewId()) 325 | 326 | let defineConstants = 327 | projectInstance.GetPropertyValue Property.DefineConstants 328 | |> PropertyConverter.toDefineConstants 329 | 330 | 331 | let projectName = projectInstance.TryGetPropertyValue Property.ProjectName 332 | let assemblyName = projectInstance.GetPropertyValue Property.AssemblyName 333 | let targetPath = projectInstance.GetPropertyValue Property.TargetPath 334 | let targetFramework = projectInstance.TryGetPropertyValue Property.TargetFrameworkMoniker |> Option.map FrameworkName 335 | let assemblyKeyFile = projectInstance.TryGetPropertyValue Property.AssemblyOriginatorKeyFile 336 | let signAssembly = PropertyConverter.toBoolean <| projectInstance.GetPropertyValue Property.SignAssembly 337 | let outputType = OutputType.Parse <| projectInstance.GetPropertyValue Property.OutputType 338 | let xmlDocs = projectInstance.TryGetPropertyValue Property.DocumentationFile 339 | 340 | { ProjectFilePath = projectFilePath 341 | ProjectGuid = projectGuid 342 | ProjectId = projectId 343 | Name = projectName 344 | TargetFramework = targetFramework 345 | FrameworkVersion = fxVer 346 | AssemblyName = assemblyName 347 | OutputPath = targetPath 348 | OutputType = outputType 349 | SignAssembly = signAssembly 350 | AssemblyOriginatorKeyFile = assemblyKeyFile 351 | GenerateXmlDocumentation = xmlDocs 352 | PreprocessorSymbolNames = defineConstants |> Array.ofSeq 353 | CompileFiles = sourceFiles |> Array.ofSeq 354 | PageFiles = pages |> Array.ofSeq 355 | ContentFiles = content |> Array.ofSeq 356 | ScriptFiles = scriptFiles |> Array.ofSeq 357 | ResourceFiles = resources |> Array.ofSeq 358 | EmbeddedResourceFiles = embeddedResources |> Array.ofSeq 359 | OtherFiles = otherFiles |> Array.ofSeq 360 | References = references |> Array.ofSeq 361 | ProjectReferences = projectReferences |> Array.ofSeq 362 | Analyzers = analyzers |> Array.ofSeq 363 | Options = options |> Array.ofSeq 364 | } 365 | 366 | 367 | let private createSrcDocs directory projectId filePaths srcCodeKind = 368 | filePaths |> Seq.map ^ fun path -> 369 | let fullpath = Path.Combine(directory,path) 370 | DocumentInfo.Create 371 | ( DocumentId.CreateNewId projectId 372 | , Path.GetFileNameWithoutExtension path 373 | , sourceCodeKind = srcCodeKind 374 | , filePath = fullpath 375 | , loader = FileTextLoader(fullpath,Text.Encoding.UTF8) 376 | , isGenerated = false 377 | ) 378 | 379 | let createSrcDocInfos (projectFileInfo:ProjectFileInfo) = 380 | createSrcDocs projectFileInfo.ProjectDirectory 381 | projectFileInfo.ProjectId 382 | projectFileInfo.CompileFiles 383 | SourceCodeKind.Regular 384 | 385 | let createScriptDocInfos (projectFileInfo:ProjectFileInfo) = 386 | createSrcDocs projectFileInfo.ProjectDirectory 387 | projectFileInfo.ProjectId 388 | projectFileInfo.ScriptFiles 389 | SourceCodeKind.Script 390 | 391 | let createOtherDocInfos (projectFileInfo:ProjectFileInfo) = 392 | projectFileInfo.OtherFiles |> Seq.map ^ fun path -> 393 | let fullpath = Path.Combine(projectFileInfo.ProjectDirectory,path) 394 | DocumentInfo.Create 395 | ( DocumentId.CreateNewId projectFileInfo.ProjectId 396 | , Path.GetFileNameWithoutExtension path 397 | , filePath = fullpath 398 | , loader = FileTextLoader(fullpath,Text.Encoding.UTF8) 399 | , isGenerated = false 400 | ) 401 | 402 | let createAdditionalDocuments projectFileInfo = 403 | Seq.append (createScriptDocInfos projectFileInfo) 404 | (createOtherDocInfos projectFileInfo) 405 | 406 | let createAnalyzerReferences (projectFileInfo:ProjectFileInfo) = 407 | if projectFileInfo.Analyzers.Length = 0 then Seq.empty else 408 | projectFileInfo.Analyzers |> Seq.map ^ fun path -> 409 | AnalyzerFileReference(path,AnalyzerAssemblyLoader()) 410 | :> AnalyzerReference 411 | 412 | 413 | /// Converts into the Microsoft.CodeAnalysis ProjectInfo used by workspaces 414 | // TODO - 415 | // change the internals to a recusive generation of projectInfo for all project references 416 | // without creating duplicate projects 417 | let toProjectInfo (workspace:'a when 'a :> Workspace) (projectFileInfo : ProjectFileInfo) = 418 | 419 | let projectRefs = 420 | let projIds, paths = (workspace :> Workspace).GetProjectIdsFromPaths projectFileInfo.ProjectReferences 421 | // TODO - this is a temporary impl, projectInfos need to be generated for the paths to projects 422 | // that aren't contained in the workspace 423 | Seq.append 424 | [ for projId in projIds -> ProjectReference projId ] 425 | [ for path in paths -> ProjectReference ^ ProjectId.CreateNewId() ] 426 | 427 | let projDict = workspace.ProjectDictionary() 428 | 429 | ProjectInfo.Create 430 | ( id = projectFileInfo.ProjectId 431 | , version = VersionStamp.Create() 432 | , name = defaultArg projectFileInfo.Name String.Empty 433 | , assemblyName = projectFileInfo.AssemblyName 434 | , language = Constants.FSharpLanguageName 435 | , filePath = projectFileInfo.ProjectFilePath 436 | , outputFilePath = projectFileInfo.OutputPath 437 | (* - TODO - 438 | Correctly adding projectreferences is going to be an issue 439 | ProjectReference is created using a project id, which means a collection of 440 | projectFileInfos should be passed to this function to prevent the creation 441 | of duplicate projectfile infos for referenced projects that have different ids 442 | *) 443 | , projectReferences = projectRefs 444 | , metadataReferences = seq[] 445 | , analyzerReferences = createAnalyzerReferences projectFileInfo 446 | , documents = createSrcDocInfos projectFileInfo 447 | , additionalDocuments = createAdditionalDocuments projectFileInfo 448 | //, compilationOptions= 449 | //, parseOptions= 450 | //, isSubmission= 451 | //, hostObjectType= 452 | ) 453 | 454 | 455 | open Microsoft.FSharp.Compiler.SourceCodeServices 456 | 457 | let toFSharpProjectOptions (workspace:'a when 'a :> Workspace) (projectFileInfo:ProjectFileInfo) : FSharpProjectOptions = 458 | (projectFileInfo |> toProjectInfo workspace).ToFSharpProjectOptions workspace 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | // with 467 | // static member Create (projectInfo:Proje) 468 | -------------------------------------------------------------------------------- /ProtoWorkspace/Scripts/load-project-debug.fsx: -------------------------------------------------------------------------------- 1 | // Warning: generated file; your changes could be lost when a new file is generated. 2 | #I __SOURCE_DIRECTORY__ 3 | #load "load-references-debug.fsx" 4 | #load "../Prelude.fs" 5 | "../Extensions.fs" 6 | "../XLinq.fs" 7 | "../Environment.fs" 8 | "../Constants.fs" 9 | "../Utilities.fs" 10 | "../MSBuildInfo.fs" 11 | "../ProjectFileInfo.fs" 12 | "../SolutionSystem.fs" 13 | "../BufferTracker.fs" 14 | "../FileSystem.fs" 15 | "../ProjectConfig.fs" 16 | "../HostServices.fs" 17 | "../Workspace.fs" 18 | "../MSBuildProjectSystem.fs" 19 | -------------------------------------------------------------------------------- /ProtoWorkspace/Scripts/load-project-release.fsx: -------------------------------------------------------------------------------- 1 | // Warning: generated file; your changes could be lost when a new file is generated. 2 | #I __SOURCE_DIRECTORY__ 3 | #load "load-references-release.fsx" 4 | #load "../Prelude.fs" 5 | "../Extensions.fs" 6 | "../Constants.fs" 7 | "../Environment.fs" 8 | "../Converters.fs" 9 | "../Loaders.fs" 10 | "../Text.fs" 11 | "../Commands.fs" 12 | "../ProjectFileInfo.fs" 13 | "../SolutionFileInfo.fs" 14 | "../BufferTracker.fs" 15 | "../ProjectConfig.fs" 16 | "../HostServices.fs" 17 | "../Workspace.fs" 18 | "../LanguageService.fs" 19 | "../MSBuildConfig.fs" 20 | "../MSBuildProjectSystem.fs" 21 | "../WorkspaceServices.fs" 22 | "../Services/ServiceInterfaces.fs" 23 | "../Services/Highlighting.fs" 24 | "../Services/QuickInfo.fs" 25 | "../Services/BraceMatching.fs" 26 | -------------------------------------------------------------------------------- /ProtoWorkspace/Scripts/load-references-debug.fsx: -------------------------------------------------------------------------------- 1 | // Warning: generated file; your changes could be lost when a new file is generated. 2 | #I __SOURCE_DIRECTORY__ 3 | #r "System.ComponentModel.Composition.dll" 4 | #r "System.Core.dll" 5 | #r "System.dll" 6 | #r "System.Numerics.dll" 7 | #r "System.Xml.dll" 8 | #r "../../packages/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.dll" 9 | #r "../../packages/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.MSBuild.v12.dll" 10 | #r "../../packages/Microsoft.CodeAnalysis.Common/lib/net45/Microsoft.CodeAnalysis.dll" 11 | #r "../../packages/Microsoft.CodeAnalysis.Workspaces.Common/lib/net45/Microsoft.CodeAnalysis.Workspaces.Desktop.dll" 12 | #r "../../packages/Microsoft.CodeAnalysis.Workspaces.Common/lib/net45/Microsoft.CodeAnalysis.Workspaces.dll" 13 | #r "../../packages/Microsoft.Extensions.Caching.Abstractions/lib/netstandard1.0/Microsoft.Extensions.Caching.Abstractions.dll" 14 | #r "../../packages/Microsoft.Extensions.Caching.Memory/lib/net451/Microsoft.Extensions.Caching.Memory.dll" 15 | #r "../../packages/Microsoft.Extensions.Configuration.Abstractions/lib/netstandard1.0/Microsoft.Extensions.Configuration.Abstractions.dll" 16 | #r "../../packages/Microsoft.Extensions.Configuration/lib/netstandard1.1/Microsoft.Extensions.Configuration.dll" 17 | #r "../../packages/Microsoft.Extensions.DependencyInjection.Abstractions/lib/netstandard1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" 18 | #r "../../packages/Microsoft.Extensions.FileSystemGlobbing/lib/net451/Microsoft.Extensions.FileSystemGlobbing.dll" 19 | #r "../../packages/Microsoft.Extensions.Logging.Abstractions/lib/netstandard1.1/Microsoft.Extensions.Logging.Abstractions.dll" 20 | #r "../../packages/Microsoft.Extensions.Logging.Console/lib/net451/Microsoft.Extensions.Logging.Console.dll" 21 | #r "../../packages/Microsoft.Extensions.Logging/lib/netstandard1.1/Microsoft.Extensions.Logging.dll" 22 | #r "../../packages/Microsoft.Extensions.Options/lib/netstandard1.0/Microsoft.Extensions.Options.dll" 23 | #r "../../packages/Microsoft.Extensions.PlatformAbstractions/lib/net451/Microsoft.Extensions.PlatformAbstractions.dll" 24 | #r "../../packages/System.AppContext/ref/net46/System.AppContext.dll" 25 | #r "../../packages/System.Collections.Immutable/lib/netstandard1.0/System.Collections.Immutable.dll" 26 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.AttributedModel.dll" 27 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.Convention.dll" 28 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.Hosting.dll" 29 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.Runtime.dll" 30 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.TypedParts.dll" 31 | #r "../../packages/System.Console/ref/net46/System.Console.dll" 32 | #r "../../packages/System.Diagnostics.FileVersionInfo/ref/net46/System.Diagnostics.FileVersionInfo.dll" 33 | #r "../../packages/System.Diagnostics.StackTrace/ref/net46/System.Diagnostics.StackTrace.dll" 34 | #r "../../packages/System.IO.FileSystem/ref/net46/System.IO.FileSystem.dll" 35 | #r "../../packages/System.IO.FileSystem.Primitives/ref/net46/System.IO.FileSystem.Primitives.dll" 36 | #r "../../packages/System.IO.FileSystem.Watcher/ref/net46/System.IO.FileSystem.Watcher.dll" 37 | #r "../../packages/System.Reflection.Metadata/lib/netstandard1.1/System.Reflection.Metadata.dll" 38 | #r "../../packages/System.Runtime.InteropServices.RuntimeInformation/ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll" 39 | #r "../../packages/System.Security.Cryptography.Algorithms/ref/net461/System.Security.Cryptography.Algorithms.dll" 40 | #r "../../packages/System.Security.Cryptography.Encoding/ref/net46/System.Security.Cryptography.Encoding.dll" 41 | #r "../../packages/System.Security.Cryptography.Primitives/ref/net46/System.Security.Cryptography.Primitives.dll" 42 | #r "../../packages/System.Security.Cryptography.X509Certificates/ref/net461/System.Security.Cryptography.X509Certificates.dll" 43 | #r "../../packages/System.Text.Encoding.CodePages/ref/netstandard1.3/System.Text.Encoding.CodePages.dll" 44 | #r "../../packages/System.Threading.Thread/ref/net46/System.Threading.Thread.dll" 45 | #r "System.Xml.Linq.dll" 46 | #r "../../packages/System.Xml.XmlDocument/ref/net46/System.Xml.XmlDocument.dll" 47 | #r "../../packages/System.Xml.XPath/ref/net46/System.Xml.XPath.dll" 48 | #r "../../packages/System.Xml.XPath.XDocument/ref/net46/System.Xml.XPath.XDocument.dll" 49 | #r "Microsoft.Build.dll" 50 | #r "Microsoft.Build.Framework.dll" 51 | #r "Microsoft.Build.Tasks.v4.0.dll" 52 | #r "Microsoft.Build.Utilities.v4.0.dll" -------------------------------------------------------------------------------- /ProtoWorkspace/Scripts/load-references-release.fsx: -------------------------------------------------------------------------------- 1 | // Warning: generated file; your changes could be lost when a new file is generated. 2 | #I __SOURCE_DIRECTORY__ 3 | #r "System.Core.dll" 4 | #r "System.dll" 5 | #r "System.Numerics.dll" 6 | #r "../../packages/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.dll" 7 | #r "../../packages/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.MSBuild.v12.dll" 8 | #r "../../packages/Microsoft.CodeAnalysis.Common/lib/net45/Microsoft.CodeAnalysis.dll" 9 | #r "../../packages/Microsoft.CodeAnalysis.Workspaces.Common/lib/net45/Microsoft.CodeAnalysis.Workspaces.Desktop.dll" 10 | #r "../../packages/Microsoft.CodeAnalysis.Workspaces.Common/lib/net45/Microsoft.CodeAnalysis.Workspaces.dll" 11 | #r "../../packages/Microsoft.Extensions.Caching.Abstractions/lib/netstandard1.0/Microsoft.Extensions.Caching.Abstractions.dll" 12 | #r "../../packages/Microsoft.Extensions.Caching.Memory/lib/net451/Microsoft.Extensions.Caching.Memory.dll" 13 | #r "../../packages/Microsoft.Extensions.Configuration.Abstractions/lib/netstandard1.0/Microsoft.Extensions.Configuration.Abstractions.dll" 14 | #r "../../packages/Microsoft.Extensions.Configuration/lib/netstandard1.1/Microsoft.Extensions.Configuration.dll" 15 | #r "../../packages/Microsoft.Extensions.DependencyInjection.Abstractions/lib/netstandard1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" 16 | #r "../../packages/Microsoft.Extensions.FileSystemGlobbing/lib/net451/Microsoft.Extensions.FileSystemGlobbing.dll" 17 | #r "../../packages/Microsoft.Extensions.Logging.Abstractions/lib/netstandard1.1/Microsoft.Extensions.Logging.Abstractions.dll" 18 | #r "../../packages/Microsoft.Extensions.Logging.Console/lib/net451/Microsoft.Extensions.Logging.Console.dll" 19 | #r "../../packages/Microsoft.Extensions.Logging/lib/netstandard1.1/Microsoft.Extensions.Logging.dll" 20 | #r "../../packages/Microsoft.Extensions.Options/lib/netstandard1.0/Microsoft.Extensions.Options.dll" 21 | #r "../../packages/Microsoft.Extensions.PlatformAbstractions/lib/net451/Microsoft.Extensions.PlatformAbstractions.dll" 22 | #r "../../packages/System.AppContext/ref/net46/System.AppContext.dll" 23 | #r "../../packages/System.Collections.Immutable/lib/netstandard1.0/System.Collections.Immutable.dll" 24 | #r "System.ComponentModel.Composition.dll" 25 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.AttributedModel.dll" 26 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.Convention.dll" 27 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.Hosting.dll" 28 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.Runtime.dll" 29 | #r "../../packages/Microsoft.Composition/lib/portable-net45+win8+wp8+wpa81/System.Composition.TypedParts.dll" 30 | #r "../../packages/System.Console/ref/net46/System.Console.dll" 31 | #r "../../packages/System.Diagnostics.FileVersionInfo/ref/net46/System.Diagnostics.FileVersionInfo.dll" 32 | #r "../../packages/System.Diagnostics.StackTrace/ref/net46/System.Diagnostics.StackTrace.dll" 33 | #r "../../packages/System.IO.FileSystem/ref/net46/System.IO.FileSystem.dll" 34 | #r "../../packages/System.IO.FileSystem.Primitives/ref/net46/System.IO.FileSystem.Primitives.dll" 35 | #r "../../packages/System.IO.FileSystem.Watcher/ref/net46/System.IO.FileSystem.Watcher.dll" 36 | #r "../../packages/System.Reflection.Metadata/lib/netstandard1.1/System.Reflection.Metadata.dll" 37 | #r "../../packages/System.Runtime.InteropServices.RuntimeInformation/ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll" 38 | #r "../../packages/System.Security.Cryptography.Encoding/ref/net46/System.Security.Cryptography.Encoding.dll" 39 | #r "../../packages/System.Security.Cryptography.Primitives/ref/net46/System.Security.Cryptography.Primitives.dll" 40 | #r "../../packages/System.Text.Encoding.CodePages/ref/netstandard1.3/System.Text.Encoding.CodePages.dll" 41 | #r "../../packages/System.Threading.Thread/ref/net46/System.Threading.Thread.dll" 42 | #r "System.Xml.dll" 43 | #r "System.Xml.Linq.dll" 44 | #r "../../packages/System.Xml.XmlDocument/ref/net46/System.Xml.XmlDocument.dll" 45 | #r "../../packages/System.Xml.XPath/ref/net46/System.Xml.XPath.dll" 46 | #r "../../packages/System.Xml.XPath.XDocument/ref/net46/System.Xml.XPath.XDocument.dll" 47 | #r "System.Configuration.dll" 48 | #r "../../packages/FSharpVSPowerTools.Core/lib/net45/FSharpVSPowerTools.Core.dll" 49 | #r "../../packages/System.Security.Cryptography.Algorithms/ref/net46/System.Security.Cryptography.Algorithms.dll" 50 | #r "../../packages/System.Security.Cryptography.X509Certificates/ref/net46/System.Security.Cryptography.X509Certificates.dll" 51 | #r "../../packages/Microsoft.Build/ref/net45/Microsoft.Build.dll" 52 | #r "../../packages/Microsoft.Build.Framework/ref/net45/Microsoft.Build.Framework.dll" 53 | #r "../../packages/Microsoft.Build.Tasks.Core/ref/net45/Microsoft.Build.Tasks.Core.dll" 54 | #r "../../packages/Microsoft.Build.Utilities.Core/ref/net45/Microsoft.Build.Utilities.Core.dll" 55 | #r "../../packages/Newtonsoft.Json/lib/net45/Newtonsoft.Json.dll" -------------------------------------------------------------------------------- /ProtoWorkspace/Services/BraceMatching.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.BraceMatching 2 | 3 | open System 4 | open Microsoft.CodeAnalysis 5 | open Microsoft.CodeAnalysis.Text 6 | open Microsoft.FSharp.Compiler 7 | open Microsoft.FSharp.Compiler.SourceCodeServices 8 | open FSharpVSPowerTools 9 | open ProtoWorkspace.ServiceInterfaces 10 | open ProtoWorkspace.LanguageService 11 | 12 | 13 | [] 14 | type FSharpBraceMatchingService () = 15 | 16 | static member GetBraceMatchingResult (sourceText, fileName, options, position) = async { 17 | let isPositionInRange range = 18 | let span = Converters.fsharpRangeToTextSpan (sourceText, range) 19 | span.Start <= position && position < span.End 20 | let! matchedBraces = Checker.MatchBracesAlternate (fileName, sourceText.ToString(), options) 21 | 22 | return 23 | matchedBraces |> Seq.tryFind ^ fun (left, right) -> isPositionInRange left || isPositionInRange right 24 | } 25 | 26 | interface IBraceMatcher with 27 | member this.FindBracesAsync (document, position, cancellationToken) = async { 28 | match FSharpLanguageService.GetOptions document.Project.Id with 29 | | Some options -> 30 | let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask 31 | let! result = FSharpBraceMatchingService.GetBraceMatchingResult (sourceText, document.Name, options, position) 32 | return 33 | match result with 34 | | None -> None 35 | | Some (left, right) -> 36 | Some ^ braceMatchingResult 37 | (Converters.fsharpRangeToTextSpan (sourceText, left)) 38 | (Converters.fsharpRangeToTextSpan (sourceText, right)) 39 | | None -> return None 40 | } -------------------------------------------------------------------------------- /ProtoWorkspace/Services/Highlighting.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.Highlighting 2 | 3 | 4 | open System 5 | open System.Threading.Tasks 6 | open Microsoft.CodeAnalysis 7 | open Microsoft.CodeAnalysis.Classification 8 | open Microsoft.CodeAnalysis.Text 9 | open Microsoft.CodeAnalysis.Editing 10 | 11 | 12 | //type FSharpHighlighter () = 13 | // 14 | // interface 15 | // 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ProtoWorkspace/Services/QuickInfo.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.QuickInfo 2 | 3 | open System 4 | open System.Threading.Tasks 5 | open Microsoft.CodeAnalysis 6 | open Microsoft.CodeAnalysis.Classification 7 | open Microsoft.CodeAnalysis.Text 8 | open Microsoft.CodeAnalysis.Editing 9 | 10 | 11 | -------------------------------------------------------------------------------- /ProtoWorkspace/Services/ServiceInterfaces.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.ServiceInterfaces 2 | 3 | open System 4 | open System.Composition 5 | open System.Threading 6 | open System.Threading.Tasks 7 | open Microsoft.CodeAnalysis 8 | open Microsoft.CodeAnalysis.Text 9 | open Microsoft.CodeAnalysis.Host 10 | open Microsoft.CodeAnalysis.Classification 11 | open Microsoft.CodeAnalysis.Formatting 12 | open Microsoft.FSharp.Compiler.SourceCodeServices 13 | 14 | 15 | /// TODO - delete this later, temporary usages for impl 16 | let Checker = FSharpChecker.Create () 17 | 18 | 19 | 20 | type [] BraceMatchingResult (leftSpan:TextSpan, rightSpan:TextSpan) = 21 | member __.LeftSpan = leftSpan 22 | member __.RightSpan = rightSpan 23 | 24 | 25 | let braceMatchingResult leftSpan rightSpan = BraceMatchingResult (leftSpan, rightSpan) 26 | 27 | 28 | type IBraceMatcher = 29 | abstract FindBracesAsync: 30 | document:Document * position:int * cancellationToken:CancellationToken -> Async 31 | 32 | 33 | type IBraceMatchingService = 34 | abstract member GetMatchingBracesAsync: 35 | document:Document * position:int * cancellationToken:CancellationToken -> Async 36 | 37 | 38 | [] 39 | type ExportBraceMatcherAttribute (language:string) = 40 | inherit ExportAttribute (typeof) 41 | do 42 | if isNull language then raise ^ ArgumentNullException language 43 | member __.Language = language 44 | 45 | 46 | 47 | type IHighlightingService = 48 | abstract member GetHighlights: position:int -> TextSpan seq 49 | 50 | 51 | 52 | /// 53 | /// An indentation result represents where the indent should be placed. It conveys this through 54 | /// a pair of values. A position in the existing document where the indent should be relative, 55 | /// and the number of columns after that the indent should be placed at. 56 | /// 57 | /// This pairing provides flexibility to the implementor to compute the indentation results in 58 | /// a variety of ways. For example, one implementation may wish to express indentation of a 59 | /// newline as being four columns past the start of the first token on a previous line. Another 60 | /// may wish to simply express the indentation as an absolute amount from the start of the 61 | /// current line. With this tuple, both forms can be expressed, and the implementor does not 62 | /// have to convert from one to the other. 63 | /// 64 | [] 65 | type IndentationResult (basePosition:int, offset:int) = 66 | /// 67 | /// The base position in the document that the indent should be relative to. This position 68 | /// can occur on any line (including the current line, or a previous line). 69 | /// 70 | member __.BasePosition = basePosition 71 | 72 | /// 73 | /// The number of columns the indent should be at relative to the BasePosition's column. 74 | /// 75 | member __.Offset = offset 76 | 77 | 78 | 79 | type IIndentationService = 80 | inherit ILanguageService 81 | abstract member GetDesiredIndentation: 82 | document:Document * lineNumber:int * cancellationToken:CancellationToken -> Async 83 | 84 | 85 | type ISynchronousIndentationService = 86 | inherit ILanguageService 87 | abstract member GetDesiredIndentation: 88 | document:Document * lineNumber:int * cancellationToken:CancellationToken -> Async 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ProtoWorkspace/SolutionFileInfo.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | open System 4 | open System.IO 5 | open System.Text 6 | open Microsoft.CodeAnalysis 7 | 8 | type LineScanner(line : string) = 9 | let mutable line = line 10 | let mutable currentPosition = 0 11 | 12 | /// Return the text following the scanner's current position 13 | member __.ReadRest() = 14 | let rest = line.Substring currentPosition 15 | currentPosition <- line.Length 16 | rest 17 | 18 | /// Return the text between the scanner's current position and 19 | /// the provided delimiter. 20 | member self.ReadUpToAndEat(delimiter : string) = 21 | match line.IndexOf(delimiter, currentPosition) with 22 | | -1 -> self.ReadRest() 23 | | index -> 24 | let upToDelimiter = line.Substring(currentPosition, index - currentPosition) 25 | currentPosition <- index + delimiter.Length 26 | upToDelimiter 27 | 28 | type SectionBlock = { 29 | BlockType : string 30 | ParenthesizedName : string 31 | Value : string 32 | KeyMap : (string * string) [] 33 | } 34 | 35 | 36 | [] 37 | module SectionBlock = 38 | let parse (reader : TextReader) = 39 | let rec findStart() = 40 | match reader.ReadLine() with 41 | | null -> failwith "found null while parsing a section block" 42 | | line -> 43 | let startline = line.TrimStart [||] 44 | if startline <> String.Empty then startline 45 | else findStart() 46 | 47 | let startline = findStart() 48 | let scanner = LineScanner startline 49 | let blockType = scanner.ReadUpToAndEat "(" 50 | let parenthesizedName = scanner.ReadUpToAndEat ") = " 51 | let sectionValue = scanner.ReadRest() 52 | printfn "section blocktype is - %s" blockType 53 | printfn "End%s" blockType 54 | let rec findPairs() = [| 55 | match reader.ReadLine() with 56 | | null -> () 57 | | line -> 58 | match line.TrimStart [||] with 59 | | txt when txt = "End" + blockType -> () 60 | | "" -> yield! findPairs() 61 | | line -> 62 | let scanner = LineScanner line 63 | let key = scanner.ReadUpToAndEat " = " 64 | let value = scanner.ReadRest() 65 | yield (key, value) 66 | yield! findPairs() 67 | |] 68 | 69 | let keyMap = findPairs() 70 | { BlockType = blockType 71 | ParenthesizedName = parenthesizedName 72 | Value = sectionValue 73 | KeyMap = keyMap 74 | } 75 | 76 | let getText indent (block : SectionBlock) = 77 | let builder = 78 | StringBuilder().Append('\t', indent).Append(sprintf "%s(%s) = " block.BlockType block.ParenthesizedName) 79 | .AppendLine block.Value 80 | for (key, value) in block.KeyMap do 81 | builder.Append('\t', indent + 1).Append(key).Append(" = ").AppendLine value |> ignore 82 | builder.Append('\t', indent).Append(sprintf "End%s" block.BlockType).AppendLine() |> string 83 | 84 | type ProjectBlock = 85 | { ProjectTypeGuid : Guid 86 | ProjectName : string 87 | ProjectPath : string 88 | ProjectGuid : Guid 89 | ProjectSections : SectionBlock [] } 90 | 91 | [] 92 | module ProjectBlock = 93 | let parse (reader : TextReader) : ProjectBlock = 94 | let startLine = reader.ReadLine().TrimStart [||] 95 | let scanner = LineScanner startLine 96 | let invalid str = raise <| exn (sprintf "Invalid Project Block in Solution - expected %s" str) 97 | if scanner.ReadUpToAndEat "(\"" <> "Project" then invalid "Project" 98 | let projectTypeGuid = Guid.Parse <| scanner.ReadUpToAndEat "\")" 99 | if scanner.ReadUpToAndEat("\"").Trim() <> "=" then invalid "=" 100 | let projectName = scanner.ReadUpToAndEat "\"" 101 | if scanner.ReadUpToAndEat("\"").Trim() <> "," then invalid "," 102 | let projectPath = scanner.ReadUpToAndEat "\"" 103 | if scanner.ReadUpToAndEat("\"").Trim() <> "," then invalid "," 104 | let projectGuid = Guid.Parse <| scanner.ReadUpToAndEat "\"" 105 | 106 | let rec getSections() = 107 | [| if not (Char.IsWhiteSpace <| char (reader.Peek())) then () 108 | else 109 | yield SectionBlock.parse reader 110 | yield! getSections() |] 111 | 112 | let projectSections = getSections() 113 | let peekCharNot c = (reader.Peek() |> char) <> c 114 | // Expect to see "EndProject" but be tolerant with missing tags as in Dev12. 115 | // Instead, we may see either P' for "Project" or 'G' for "Global", which will be handled next. 116 | if peekCharNot 'P' && peekCharNot 'G' then 117 | if reader.ReadLine() <> "EndProject" then invalid "EndProject" 118 | { ProjectTypeGuid = projectTypeGuid 119 | ProjectName = projectName 120 | ProjectPath = projectPath 121 | ProjectGuid = projectGuid 122 | ProjectSections = projectSections } 123 | 124 | 125 | 126 | let getText (projectBlock : ProjectBlock) = 127 | let typeGuid = projectBlock.ProjectTypeGuid.ToString("B").ToUpper() 128 | let projGuid = projectBlock.ProjectGuid.ToString("B").ToUpper() 129 | let builder = 130 | StringBuilder() 131 | .Append(sprintf "Project(\"%s\") = \"%s\", \"%s\", \"%s\"" typeGuid projectBlock.ProjectName 132 | projectBlock.ProjectPath projGuid).AppendLine() 133 | for section in projectBlock.ProjectSections do 134 | SectionBlock.getText 1 section 135 | |> builder.Append 136 | |> ignore 137 | builder.AppendLine "EndProject" |> string 138 | 139 | let toProjectInfo (workspace:'a when 'a :> Workspace) (slnDirectory:string) (block: ProjectBlock) : ProjectInfo = 140 | let path = Path.Combine(slnDirectory,block.ProjectPath) 141 | // let fileinfo = ProjectFileInfo.fromXDoc (block.ProjectPath |> Path.GetFullPath) 142 | let fileinfo = ProjectFileInfo.create (block.ProjectPath |> Path.GetFullPath) 143 | ProjectFileInfo.toProjectInfo workspace fileinfo 144 | // ProjectInfo.Create 145 | // ( id = ProjectId.CreateFromSerialized block.ProjectGuid 146 | // , version = VersionStamp.Create() 147 | // , name = block.ProjectName 148 | // , assemblyName = "fake assembly name" 149 | // , language = "en-us" 150 | // , filePath = (block.ProjectPath |> Path.GetFullPath) 151 | // ?outputFilePath:string 152 | // ?compilationOptions:CompilationOptions 153 | // ?parseOptions:ParseOptions 154 | // ?documents:IEnumerable 155 | // ?projectReferences:IEnumerable 156 | // ?metadataReferences:IEnumerable 157 | // ?analyzerReferences:IEnumerable 158 | // ?additionalDocuments:IEnumerable 159 | // ?isSubmission:bool 160 | // ?hostObjectType:Type 161 | // ) 162 | 163 | // let toProjectInfo (projectBlock:ProjectBlock) = 164 | // ProjectInfo. 165 | // let p = projectBlock 166 | type SolutionFileInfo = 167 | { Path : string 168 | HeaderLines : string [] 169 | VSVersionLineOpt : string 170 | MinVSVersionLineOpt : string 171 | ProjectBlocks : ProjectBlock [] 172 | GlobalSectionBlocks : SectionBlock [] } 173 | member self.Directory = (FileInfo self.Path).Directory.FullName 174 | // NOTE - I'm not a fan of this parsing approach, 175 | 176 | [] 177 | module SolutionFileInfo = 178 | let getText (sln : SolutionFileInfo) = 179 | let builder = StringBuilder().AppendLine() 180 | for line in sln.HeaderLines do 181 | builder.AppendLine line |> ignore 182 | for block in sln.ProjectBlocks do 183 | block 184 | |> ProjectBlock.getText 185 | |> builder.Append 186 | |> ignore 187 | builder.AppendLine "Global" |> ignore 188 | for section in sln.GlobalSectionBlocks do 189 | section 190 | |> SectionBlock.getText 1 191 | |> builder.Append 192 | |> ignore 193 | builder.AppendLine "EndGlobal" |> string 194 | 195 | let private getNextNonEmptyLine (reader : TextReader) = 196 | let rec getLine (line : string) = 197 | if isNull line || line.Trim() <> String.Empty then line 198 | else getLine <| reader.ReadLine() 199 | getLine <| reader.ReadLine() 200 | 201 | let private consumeEmptyLines (reader : TextReader) = 202 | while reader.Peek() <> -1 && "\r\n".Contains(reader.Peek()|> char|> string) do 203 | reader.ReadLine() |> ignore 204 | 205 | let inline private parseError expected actual = 206 | raise ^ exn ^ sprintf "invalid global section - expected '%s', was - '%s'" expected actual 207 | 208 | let private parseCheck expected actual = 209 | if expected <> actual then parseError expected actual 210 | 211 | let private parseGlobal (reader : TextReader) : SectionBlock [] = 212 | if reader.Peek() = -1 then [||] 213 | else 214 | let firstline = getNextNonEmptyLine reader 215 | // printfn "%s" firstline 216 | parseCheck "Global" firstline 217 | let rec getBlocks() = [| 218 | if reader.Peek() = -1 || not (Char.IsWhiteSpace(reader.Peek() |> char)) then () 219 | else 220 | yield SectionBlock.parse reader 221 | yield! getBlocks() 222 | |] 223 | 224 | let globalSectionBlocks = getBlocks() 225 | // printfn "%A" globalSectionBlocks 226 | parseCheck "EndGlobal" (getNextNonEmptyLine reader) 227 | consumeEmptyLines reader 228 | globalSectionBlocks 229 | 230 | let private parse path (reader:TextReader) = 231 | let headerLine1 = getNextNonEmptyLine reader 232 | if isNull headerLine1 || not (headerLine1.StartsWith ^ "Microsoft Visual Studio Solution File") then 233 | parseError "Microsoft Visual Studio Solution File" headerLine1 234 | /// skip comment lines and empty lines 235 | let rec getLines () = 236 | [| // finish if not a commentline, empty line, or if it's the end of the file 237 | if reader.Peek() = -1 || not (Array.contains (reader.Peek() |> char) [| '#'; '\r'; '\n' |]) then () 238 | else if reader.Peek() = -1 then () 239 | else 240 | yield reader.ReadLine() 241 | yield! getLines() |] 242 | 243 | let hl = getLines() 244 | let headerLines = Array.append [| headerLine1 |] hl 245 | 246 | let visualStudioVersionLineOpt = 247 | if char (reader.Peek()) = 'V' then 248 | let line = getNextNonEmptyLine reader 249 | if not (line.StartsWith "VisualStudioVersion") then parseError "VisualStudioVersion" line 250 | line 251 | else String.Empty // should this be null? 252 | 253 | let minimumVisualStudioVersionLineOpt = 254 | if char (reader.Peek()) = 'M' then 255 | let line = getNextNonEmptyLine reader 256 | if not ^ line.StartsWith "MinimumVisualStudioVersion" then 257 | raise ^ exn "invalid global section - didn't start with 'MinimumVisualStudioVersion'" 258 | line 259 | else String.Empty // should this be null? 260 | 261 | // Parse project blocks while we have them 262 | let rec getBlocks() = 263 | [| if char (reader.Peek()) <> 'P' then () 264 | else 265 | yield ProjectBlock.parse reader 266 | // Comments and Empty Lines between the Project Blocks are skipped 267 | getLines() |> ignore 268 | yield! getBlocks() |] 269 | 270 | // Parse project blocks while we have them 271 | let projectBlocks = 272 | getBlocks() |> Array.map ^ fun blk -> 273 | { blk with ProjectPath = Path.Combine(Directory.fromPath path,blk.ProjectPath) } 274 | 275 | // projectBlocks |> Array.iter (printfn "%A") 276 | // We now have a global block 277 | let globalSectionBlocks = parseGlobal reader 278 | if reader.Peek() <> -1 then raise ^ exn "Should be at the end of file" 279 | { Path = path 280 | HeaderLines = headerLines 281 | VSVersionLineOpt = visualStudioVersionLineOpt 282 | MinVSVersionLineOpt = minimumVisualStudioVersionLineOpt 283 | ProjectBlocks = projectBlocks 284 | GlobalSectionBlocks = globalSectionBlocks 285 | } 286 | 287 | /// Loads the solution file at path as a filestream 288 | let loadFile (path : string) = 289 | use stream = File.OpenRead path 290 | use reader = new StreamReader(stream) 291 | parse path reader 292 | 293 | /// Loads the solution file at path as a string closes file and reads the string 294 | let load (path : string) = 295 | let path = Path.GetFullPath path 296 | use reader = new StringReader(File.ReadAllText path) 297 | parse path reader 298 | 299 | let toSolutionInfo (workspace:'a when 'a :> Workspace) (solutionFile: SolutionFileInfo) : SolutionInfo = 300 | 301 | let projectInfos = 302 | solutionFile.ProjectBlocks 303 | |> Array.filter ^ fun block -> not(block.ProjectTypeGuid = Constants.SolutionFolderGuid) 304 | |> Array.map ^ ProjectBlock.toProjectInfo workspace solutionFile.Directory 305 | 306 | SolutionInfo.Create( 307 | SolutionId.CreateNewId(), 308 | VersionStamp.Create(), 309 | solutionFile.Path, 310 | projectInfos 311 | ) 312 | 313 | -------------------------------------------------------------------------------- /ProtoWorkspace/Text.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.Text 2 | 3 | open System 4 | open System.Collections.Generic 5 | open Microsoft.CodeAnalysis 6 | open Microsoft.CodeAnalysis.Text 7 | open Microsoft.CodeAnalysis.Classification 8 | open Microsoft.CodeAnalysis.Editing 9 | open Microsoft.CodeAnalysis.Differencing 10 | open Newtonsoft.Json 11 | 12 | type LinePositionSpanTextChange () = 13 | 14 | member val NewText : string = "" with get, set 15 | [)>] 16 | member val StartLine : int = 0 with get, set 17 | [)>] 18 | member val StartColumn : int = 0 with get, set 19 | [)>] 20 | member val EndLine : int = 0 with get, set 21 | [)>] 22 | member val EndColumn : int = 0 with get, set 23 | 24 | override self.Equals obj = 25 | match obj with 26 | | :? LinePositionSpanTextChange as other -> 27 | self.NewText = other.NewText 28 | && self.StartLine = other.StartLine 29 | && self.StartColumn = other.StartColumn 30 | && self.EndLine = other.EndLine 31 | && self.EndColumn = other.EndColumn 32 | | _ -> false 33 | 34 | 35 | override self.GetHashCode() = 36 | self.NewText.GetHashCode() 37 | * (23 + self.StartLine) 38 | * (29 + self.StartColumn) 39 | * (31 + self.EndLine) 40 | * (37 + self.EndColumn) 41 | 42 | 43 | override self.ToString () = 44 | if isNull self.NewText then String.Empty else 45 | let displayText = self.NewText.Replace("\r", @"\r").Replace("\n", @"\n").Replace("\t", @"\t") 46 | sprintf "StartLine=%i, StartColumn=%i, Endline=%i, EndColumn=%i,NewText='%s'" 47 | self.StartLine self.StartColumn self.EndLine self.EndColumn self.NewText 48 | 49 | 50 | member __.Convert (document:Document, changes: TextChange seq) : LinePositionSpanTextChange seq Async = async { 51 | let! (text:SourceText) = document.GetTextAsync() 52 | 53 | return changes 54 | |> Seq.sortWithDescending ^ fun c1 c2 -> c1.Span.CompareTo c2.Span 55 | |> Seq.map ^ fun change -> 56 | let span = change.Span 57 | let newText = change.NewText 58 | let span, prefix, suffix = 59 | if newText.Length <= 0 then span, String.Empty, String.Empty else 60 | // Roslyn computes text changes on character arrays. So it might happen that a 61 | // change starts inbetween \r\n which is OK when you are offset-based but a problem 62 | // when you are line,column-based. This code extends text edits which just overlap 63 | // a with a line break to its full line break 64 | let span, prefix = 65 | if span.Start > 0 && newText.[0] = '\n' && text.[span.Start - 1] = '\r' then 66 | // text: foo\r\nbar\r\nfoo 67 | // edit: [----) 68 | TextSpan.FromBounds(span.Start - 1, span.End), "\r" 69 | else 70 | span, String.Empty 71 | let span, suffix = 72 | if span.End < text.Length - 1 && newText.[newText.Length - 1] = '\r' && text.[span.End] = '\n' then 73 | // text: foo\r\nbar\r\nfoo 74 | // edit: [----) 75 | TextSpan.FromBounds(span.Start, span.End + 1), "\n" 76 | else 77 | span, String.Empty 78 | span, prefix, suffix 79 | let linePositionSpan = text.Lines.GetLinePositionSpan span 80 | LinePositionSpanTextChange 81 | ( NewText = prefix + newText + suffix 82 | , StartLine = linePositionSpan.Start.Line 83 | , StartColumn = linePositionSpan.Start.Character 84 | , EndLine = linePositionSpan.End.Line 85 | , EndColumn = linePositionSpan.End.Character 86 | ) 87 | } 88 | 89 | 90 | type QuickFix () = 91 | member val Text : string = "" with get, set 92 | member val FileName : string = "" with get, set 93 | [)>] 94 | member val Line : int = 0 with get, set 95 | [)>] 96 | member val Column : int = 0 with get, set 97 | [)>] 98 | member val EndLine : int = 0 with get, set 99 | [)>] 100 | member val EndColumn : int = 0 with get, set 101 | // NOTE - this is an ICollection in Omnisharp 102 | member val Projects : string [] = [||] with get, set 103 | 104 | override self.Equals other = 105 | if isNull other then false else 106 | match other with 107 | | :? QuickFix as other -> 108 | self.FileName = other.FileName 109 | && self.Line = other.Line 110 | && self.Column = other.Column 111 | && self.EndLine = other.EndLine 112 | && self.EndColumn = other.EndColumn 113 | && self.Text = other.Text 114 | | _ -> false 115 | 116 | override self.GetHashCode () = 117 | 17 * 23 + (hash self.FileName) 118 | |> (*) 23 |> (+) ^ hash self.Line 119 | |> (*) 23 |> (+) ^ hash self.Column 120 | |> (*) 23 |> (+) ^ hash self.EndLine 121 | |> (*) 23 |> (+) ^ hash self.EndColumn 122 | |> (*) 23 |> (+) ^ hash self.Text 123 | 124 | 125 | 126 | // 127 | //type QuickFix = { 128 | // Text : string 129 | // FileName : string 130 | // [)>] 131 | // Line : int 132 | // [)>] 133 | // Column : int 134 | // [)>] 135 | // EndLine : int 136 | // [)>] 137 | // EndColumn : int 138 | // Projects : string seq 139 | //} 140 | 141 | type SymbolLocation () = 142 | inherit QuickFix() 143 | member val Kind = "" with get, set 144 | 145 | 146 | type SyntaxFeature = { 147 | Name : string 148 | Data : string 149 | } 150 | 151 | 152 | type FileMemberElement () = 153 | member val ChildNodes : FileMemberElement seq = Seq.empty with get, set 154 | member val Location = QuickFix() with get, set 155 | member val Kind = "" with get, set 156 | member val Features : SyntaxFeature seq = Seq.empty with get 157 | // NOTE - this is an ICollection in Omnisharp 158 | member val Projects : string [] = [||] with get, set 159 | 160 | member self.CompareTo (other:FileMemberElement) = 161 | if other.Location.Line < self.Location.Line then 1 162 | elif other.Location.Line > self.Location.Line then -1 163 | elif other.Location.Column < self.Location.Column then 1 164 | elif other.Location.Column > self.Location.Column then -1 165 | elif other.Location.EndLine < self.Location.EndLine then 1 166 | elif other.Location.EndLine > self.Location.EndLine then -1 167 | elif other.Location.EndColumn < self.Location.EndColumn then 1 168 | elif other.Location.EndColumn > self.Location.EndColumn then -1 169 | else 0 170 | 171 | override self.Equals (other:obj) = 172 | if isNull other then false else 173 | match other with 174 | | :? FileMemberElement as other -> 175 | self.Location.Line = other.Location.Line 176 | && self.Location.Column = other.Location.Column 177 | && self.Location.EndLine = other.Location.EndLine 178 | && self.Location.EndColumn = other.Location.EndColumn 179 | | _ -> false 180 | 181 | override self.GetHashCode() = 182 | 13 * self.Location.Line + 183 | 17 * self.Location.Column + 184 | 23 * self.Location.EndLine + 185 | 31 * self.Location.EndColumn 186 | 187 | interface IComparable with 188 | member self.CompareTo (other:obj) = 189 | match other with 190 | | :? FileMemberElement as other -> self.CompareTo other 191 | | _ -> failwith "'other' was not a FileMemberElement" 192 | 193 | 194 | type FileMemberTree () = 195 | member val TopLevelTypeDefinitions : FileMemberElement seq = Seq.empty with get, set 196 | 197 | type HighlightSpan () = 198 | 199 | member val Kind : string = "" with get, set 200 | member val Projects : string seq = Seq.empty with get, set 201 | [)>] 202 | member val StartLine : int = 0 with get, set 203 | [)>] 204 | member val StartColumn : int = 0 with get, set 205 | [)>] 206 | member val EndLine : int = 0 with get, set 207 | [)>] 208 | member val EndColumn : int = 0 with get, set 209 | 210 | 211 | member self.FromClassifiedSpan(span:ClassifiedSpan, lines:TextLineCollection, projects:string seq) = 212 | let linePos = lines.GetLinePositionSpan(span.TextSpan) 213 | HighlightSpan 214 | ( Kind = span.ClassificationType 215 | , Projects = projects 216 | , StartLine = linePos.Start.Line 217 | , EndLine = linePos.End.Line 218 | , StartColumn = linePos.Start.Character 219 | , EndColumn = linePos.End.Character 220 | ) 221 | 222 | 223 | member self.CompareTo (other:HighlightSpan) = 224 | if other.StartLine < self.StartLine then 1 225 | elif other.StartLine > self.StartLine then -1 226 | elif other.StartColumn < self.StartColumn then 1 227 | elif other.StartColumn > self.StartColumn then -1 228 | elif other.EndLine < self.EndLine then 1 229 | elif other.EndLine > self.EndLine then -1 230 | elif other.EndColumn < self.EndColumn then 1 231 | elif other.EndColumn > self.EndColumn then -1 232 | else 0 233 | 234 | 235 | override self.Equals (other:obj) = 236 | if isNull other then false else 237 | match other with 238 | | :? HighlightSpan as other -> 239 | self.StartLine = other.StartLine 240 | && self.StartColumn = other.StartColumn 241 | && self.EndLine = other.EndLine 242 | && self.EndColumn = other.EndColumn 243 | | _ -> false 244 | 245 | override self.GetHashCode() = 246 | 13 * self.StartLine + 247 | 17 * self.StartColumn + 248 | 23 * self.EndLine + 249 | 31 * self.EndColumn 250 | 251 | interface IComparable with 252 | member self.CompareTo (other:obj) = 253 | match other with 254 | | :? HighlightSpan as other -> self.CompareTo other 255 | | _ -> failwith "'other' was not a HighLightSpan" 256 | 257 | 258 | type HighlightClassification = 259 | | Name = 1 260 | | Comment = 2 261 | | String = 3 262 | | Operator = 4 263 | | Punctuation = 5 264 | | Keyword = 6 265 | | Number = 7 266 | | Identifier = 8 267 | | PreprocessorKeyword = 9 268 | | ExcludedCode = 10 269 | 270 | 271 | type SignatureHelpParameter = { 272 | Name : string 273 | Label : string 274 | Documentation : string 275 | } 276 | 277 | 278 | type SignatureHelpItem = { 279 | Name : string 280 | Label : string 281 | Documentation : string 282 | Parameters : SignatureHelpParameter list 283 | } 284 | 285 | 286 | type SignatureHelp = { 287 | Signatures : SignatureHelpItem list 288 | ActiveSignature : int 289 | ActiveParameter : int 290 | } 291 | 292 | 293 | type MetadataSource = { 294 | AssemblyName : string 295 | TypeName : string 296 | ProjectName : string 297 | VersionNumber : string 298 | Language : string 299 | } 300 | 301 | 302 | type CodeAction = { 303 | Identifier : string 304 | Name : string 305 | } 306 | 307 | type Point = { 308 | [)>] 309 | Line : int 310 | [)>] 311 | Column : int 312 | } 313 | 314 | type Range = { 315 | Start : Point 316 | End : Point 317 | } 318 | 319 | 320 | -------------------------------------------------------------------------------- /ProtoWorkspace/Workspace.fs: -------------------------------------------------------------------------------- 1 | namespace ProtoWorkspace 2 | 3 | open System 4 | open System.Linq 5 | open System.Threading 6 | open System.Composition 7 | open System.IO 8 | open System.Collections.Generic 9 | open Microsoft.CodeAnalysis 10 | open Microsoft.CodeAnalysis.Text 11 | open Microsoft.CodeAnalysis.Host 12 | open Microsoft.CodeAnalysis.Host.Mef 13 | open ProtoWorkspace.HostServices 14 | open FSharpVSPowerTools 15 | open ProtoWorkspace.Text 16 | open FSharp.Control 17 | open Microsoft.FSharp.Compiler 18 | open Microsoft.FSharp.Compiler.SourceCodeServices 19 | 20 | 21 | 22 | [] 23 | type FSharpWorkspace (hostServices : HostServices) as self = 24 | inherit Workspace(hostServices,Constants.FSharpLanguageName ) 25 | let bufferManager = new BufferManager(self) 26 | let disposables = ResizeArray() 27 | let checker = FSharpChecker.Create() 28 | 29 | let checkProjectId (projectId:ProjectId) = 30 | match self.CurrentSolution.ContainsProject projectId with 31 | | false -> failwithf "The workspace does not contain a project with the id '%s'" ^ string projectId.Id 32 | | true -> () 33 | 34 | let projectOptions = Dictionary() 35 | 36 | // TODO - 37 | // What additional data caches does the workspace need? 38 | // * FSharpProjectOptions dictionary that updates on projectInfo change events? 39 | // * 40 | 41 | do 42 | disposables.Add bufferManager 43 | 44 | new (aggregator:HostServicesAggregator) = 45 | new FSharpWorkspace (aggregator.CreateHostServices()) 46 | 47 | new () = 48 | new FSharpWorkspace(FSharpHostService()) 49 | 50 | 51 | 52 | member __.Checker : FSharpChecker = checker 53 | 54 | //let activeDocuments = HashSet() 55 | //new() = new FSharpWorkspace(HostServicesAggregator(Seq.empty)) 56 | override __.CanOpenDocuments = true 57 | override __.CanApplyChange _ = true 58 | 59 | /// Adds a document to the workspace. 60 | member self.AddDocument (documentInfo:DocumentInfo) = 61 | checkNullArg documentInfo "documentInfo" 62 | base.OnDocumentAdded documentInfo 63 | self.CurrentSolution.GetDocument documentInfo.Id 64 | 65 | /// Adds a document to a project in the workspace. 66 | member __.AddDocument (projectId:ProjectId, name:string, text:SourceText) = 67 | checkNullArg projectId "projectId" 68 | checkNullArg name "name" 69 | checkNullArg text "text" 70 | DocumentInfo.Create( 71 | DocumentId.CreateNewId projectId, name, 72 | loader = TextLoader.From ^ TextAndVersion.Create(text, VersionStamp.Create()) 73 | ) 74 | 75 | 76 | /// Puts the specified document into the open state. 77 | override __.OpenDocument (docId, activate) = 78 | let doc = base.CurrentSolution.GetDocument docId 79 | if isNull doc then () 80 | else 81 | let task = doc.GetTextAsync CancellationToken.None 82 | task.Wait CancellationToken.None 83 | let text = task.Result 84 | base.OnDocumentOpened(docId, text.Container, activate) 85 | 86 | 87 | /// Puts the specified document into the closed state. 88 | override __.CloseDocument docId = 89 | let doc = base.CurrentSolution.GetDocument docId 90 | if isNull doc then () 91 | else 92 | let task = doc.GetTextAsync CancellationToken.None 93 | task.Wait CancellationToken.None 94 | let text = task.Result 95 | let versionTask = doc.GetTextVersionAsync CancellationToken.None 96 | versionTask.Wait CancellationToken.None 97 | let version = versionTask.Result 98 | let loader = TextLoader.From ^ TextAndVersion.Create(text, version, doc.FilePath) 99 | base.OnDocumentClosed(docId, loader) 100 | 101 | 102 | /// Adds a project to the workspace. All previous projects remain intact. 103 | member self.AddProject projectInfo = 104 | checkNullArg projectInfo "projectInfo" 105 | base.OnProjectAdded projectInfo 106 | base.UpdateReferencesAfterAdd() 107 | self.CurrentSolution.GetProject projectInfo.Id 108 | 109 | // /// Adds a project to the workspace. All previous projects remain intact. 110 | // member self.AddProject(name : string) = 111 | // ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), name, name, "FSharp") 112 | // |> self.AddProject 113 | 114 | /// Adds multiple projects to the workspace at once. All existing projects remain intact. 115 | member self.AddProjects (projectInfos:seq<_>) = 116 | checkNullArg projectInfos "projectInfos" 117 | for info in projectInfos do 118 | self.OnProjectAdded info 119 | base.UpdateReferencesAfterAdd() 120 | 121 | 122 | /// Adds an entire solution to the workspace, replacing any existing solution. 123 | member self.AddSolutionInfo (solutionInfo:SolutionInfo) = 124 | checkNullArg solutionInfo "solutionInfo" 125 | base.OnSolutionAdded solutionInfo 126 | base.UpdateReferencesAfterAdd() 127 | self.CurrentSolution 128 | 129 | 130 | // /// Adds an entire solution to the workspace, replacing any existing solution. 131 | // member self.AddSolution (solution:Solution) = 132 | // checkNullArg solution "solution" 133 | // let fn (arg:WorkspaceChangeEventArgs) = 134 | // arg. 135 | // base.OnSolutionAdded solution 136 | // base.UpdateReferencesAfterAdd() 137 | // self.CurrentSolution 138 | 139 | 140 | 141 | member __.AddProjectReference (projectId, projectReference) = 142 | base.OnProjectReferenceAdded (projectId, projectReference) 143 | 144 | 145 | member __.AddMetadataReference (projectId, metadataReference) = 146 | base.OnMetadataReferenceAdded (projectId, metadataReference) 147 | 148 | 149 | member __.RemoveMetadataReference (projectId, metadataReference) = 150 | base.OnMetadataReferenceRemoved (projectId, metadataReference) 151 | 152 | // member __. AddDocument documentInfo = 153 | // base.OnDocumentAdded documentInfo 154 | member __.RemoveDocument documentId = base.OnDocumentRemoved documentId 155 | 156 | member __.RemoveProject projectId = base.OnProjectRemoved projectId 157 | 158 | member __.SetCompilationOptions (projectId, options) = base.OnCompilationOptionsChanged(projectId, options) 159 | 160 | member __.SetParseOptions (projectId, parseOptions) = base.OnParseOptionsChanged(projectId, parseOptions) 161 | 162 | member __.OnDocumentChanged (documentId, text) = 163 | base.OnDocumentTextChanged(documentId, text, PreservationMode.PreserveIdentity) 164 | 165 | 166 | member __.TryGetDocumentId filePath = 167 | let documentIds = base.CurrentSolution.GetDocumentIdsWithFilePath filePath 168 | match documentIds.FirstOrDefault() with 169 | | null -> None 170 | | docId -> Some docId 171 | 172 | 173 | member self.GetDocuments filePath = 174 | base.CurrentSolution.GetDocumentIdsWithFilePath(filePath) 175 | .Select(fun docId -> self.CurrentSolution.GetDocument docId) 176 | 177 | 178 | member self.TryGetDocument filePath = 179 | self.TryGetDocumentId filePath |> Option.map (fun docId -> self.CurrentSolution.GetDocument docId) 180 | 181 | interface IDisposable with 182 | member __.Dispose() = 183 | disposables |> Seq.iter dispose 184 | disposables.Clear() 185 | 186 | and internal BufferManager (workspace:FSharpWorkspace) as self = 187 | let transientDocuments = Dictionary(StringComparer.OrdinalIgnoreCase) 188 | let transientDocumentIds = HashSet() 189 | let lockObj = obj() 190 | let workspaceEvent = workspace.WorkspaceChanged 191 | let subscriptions = ResizeArray() 192 | do 193 | workspaceEvent.Subscribe self.OnWorkspaceChanged |> subscriptions.Add 194 | 195 | let tryAddTransientDocument (fileName : string) (fileContent : string) = 196 | if String.IsNullOrWhiteSpace fileName then false 197 | else 198 | let projects = workspace.CurrentSolution.Projects 199 | if projects.Count() = 0 then false 200 | else 201 | let sourceText = SourceText.From fileContent 202 | 203 | let documents : DocumentInfo list = 204 | (projects, []) ||> Seq.foldBack ^ fun project docs -> 205 | let docId = DocumentId.CreateNewId project.Id 206 | let version = VersionStamp.Create() 207 | let docInfo = 208 | DocumentInfo.Create 209 | (docId, fileName, filePath = fileName, 210 | loader = TextLoader.From(TextAndVersion.Create(sourceText, version))) 211 | docInfo :: docs 212 | lock lockObj ^ fun () -> 213 | let docIds = documents |> List.map ^ fun doc -> doc.Id 214 | transientDocuments.Add(fileName, docIds) 215 | transientDocumentIds.UnionWith docIds 216 | documents |> List.iter ^ fun doc -> workspace.AddDocument doc |> ignore 217 | true 218 | 219 | 220 | member __.UpdateBuffer (request:Request) = async { 221 | let buffer = 222 | match request with 223 | | :? UpdateBufferRequest as request -> 224 | if request.FromDisk then File.ReadAllText request.FileName else request.Buffer 225 | | _ -> request.Buffer 226 | 227 | let changes = request.Changes 228 | let documentIds = workspace.CurrentSolution.GetDocumentIdsWithFilePath request.FileName 229 | if not documentIds.IsEmpty then 230 | if Seq.isEmpty changes then 231 | let sourceText = SourceText.From buffer 232 | documentIds |> Seq.iter ^ fun docId -> workspace.OnDocumentChanged(docId, sourceText) 233 | else 234 | for docId in documentIds do 235 | let doc = workspace.CurrentSolution.GetDocument docId 236 | let! sourceText = doc.GetTextAsync() |> Async.AwaitTask 237 | let sourceText = 238 | (sourceText, changes) ||> Seq.fold ^ fun sourceText change -> 239 | let startOffset = 240 | sourceText.Lines.GetPosition ^ LinePosition (change.StartLine, change.StartLine) 241 | let endOffset = 242 | sourceText.Lines.GetPosition ^ LinePosition (change.EndLine, change.EndColumn) 243 | sourceText.WithChanges 244 | [| TextChange(TextSpan(startOffset, endOffset - startOffset), change.NewText) |] 245 | workspace.OnDocumentChanged(docId, sourceText) 246 | else 247 | if isNotNull buffer then tryAddTransientDocument request.FileName buffer |> ignore 248 | } 249 | 250 | 251 | member __.UpdateChangeBuffer(request : ChangeBufferRequest) = async { 252 | if isNull request.FileName then () 253 | else 254 | let documentIds = workspace.CurrentSolution.GetDocumentIdsWithFilePath(request.FileName) 255 | if not documentIds.IsEmpty then 256 | for docId in documentIds do 257 | let doc = workspace.CurrentSolution.GetDocument docId 258 | let! sourceText = doc.GetTextAsync() |> Async.AwaitTask 259 | let startOffset = 260 | sourceText.Lines.GetPosition ^ LinePosition(request.StartLine, request.StartLine) 261 | let endOffset = sourceText.Lines.GetPosition ^ LinePosition(request.EndLine, request.EndColumn) 262 | let sourceText = 263 | sourceText.WithChanges 264 | [| TextChange(TextSpan(startOffset, endOffset - startOffset), request.NewText) |] 265 | workspace.OnDocumentChanged(docId, sourceText) 266 | else 267 | tryAddTransientDocument request.FileName request.NewText |> ignore 268 | } 269 | 270 | member __.OnWorkspaceChanged (args:WorkspaceChangeEventArgs) = 271 | let filename = 272 | match args.Kind with 273 | | WorkspaceChangeKind.DocumentAdded -> (args.NewSolution.GetDocument args.DocumentId).FilePath 274 | | WorkspaceChangeKind.DocumentRemoved -> (args.OldSolution.GetDocument args.DocumentId).FilePath 275 | | _ -> String.Empty 276 | if String.IsNullOrEmpty filename then () 277 | else 278 | lock lockObj ^ fun () -> 279 | match Dict.tryFind filename transientDocuments with 280 | | None -> () 281 | | Some docIds -> 282 | transientDocuments.Remove filename |> ignore 283 | for docId in docIds do 284 | workspace.RemoveDocument docId |> ignore 285 | transientDocumentIds.Remove docId |> ignore 286 | 287 | 288 | member self.FindProjectsByFileName (fileName:string) = 289 | let dirInfo = (FileInfo fileName).Directory 290 | let candidates = 291 | workspace.CurrentSolution.Projects 292 | .GroupBy(fun project -> (FileInfo project.FilePath).Directory.FullName) 293 | .ToDictionary((fun grouping -> grouping.Key),( fun grouping -> List.ofSeq grouping)) 294 | let rec loop (dirInfo:DirectoryInfo) = 295 | if isNull dirInfo then [] else 296 | match candidates.TryGetValue dirInfo.FullName with 297 | | true, ps -> ps 298 | | _, _ -> loop dirInfo.Parent 299 | loop dirInfo :> _ seq 300 | 301 | 302 | interface IDisposable with 303 | member __.Dispose() = 304 | subscriptions |> Seq.iter dispose 305 | subscriptions.Clear() 306 | -------------------------------------------------------------------------------- /ProtoWorkspace/WorkspaceServices.fs: -------------------------------------------------------------------------------- 1 | module ProtoWorkspace.WorkspaceServices 2 | 3 | open Microsoft.CodeAnalysis 4 | open Microsoft.CodeAnalysis.Classification 5 | open Microsoft.CodeAnalysis.CodeActions 6 | open Microsoft.CodeAnalysis.CodeFixes 7 | open Microsoft.CodeAnalysis.CodeRefactorings 8 | open Microsoft.CodeAnalysis.Editing 9 | open Microsoft.CodeAnalysis.Rename 10 | open Microsoft.CodeAnalysis.Options 11 | open Microsoft.CodeAnalysis.Text 12 | open Microsoft.CodeAnalysis.Formatting 13 | open Microsoft.CodeAnalysis.FindSymbols 14 | 15 | 16 | //let fn (x:CompletionProvider) = () 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ProtoWorkspace/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Compiler.Service 2 | FSharp.Core 3 | System.Collections.Immutable 4 | Microsoft.CodeAnalysis.Workspaces.Common 5 | Microsoft.Extensions.Logging 6 | Microsoft.Extensions.Configuration 7 | Microsoft.Extensions.Caching.Memory 8 | Microsoft.Extensions.FileSystemGlobbing 9 | Microsoft.Extensions.PlatformAbstractions 10 | Microsoft.Extensions.Logging.Console 11 | System.IO.FileSystem.Watcher 12 | System.Threading.Thread 13 | FSharpVSPowerTools.Core 14 | Microsoft.CodeAnalysis.Analyzers 15 | Microsoft.Build 16 | Microsoft.Build.Framework 17 | Microsoft.Build.Tasks.Core 18 | Microsoft.Build.Utilities.Core 19 | Newtonsoft.Json -------------------------------------------------------------------------------- /ProtoWorkspace/slntest.fsx: -------------------------------------------------------------------------------- 1 | System.IO.Directory.SetCurrentDirectory __SOURCE_DIRECTORY__ 2 | 3 | #load "scripts/load-references-release.fsx" 4 | #r "bin/release/protoworkspace.dll" 5 | open System.IO 6 | open ProtoWorkspace 7 | 8 | let printsq sqs = sqs|>Seq.iter^printfn"%A" 9 | 10 | let testSlnPath = "../ProtoWorkspace.sln" 11 | 12 | let fswork = new FSharpWorkspace() 13 | ;; 14 | string fswork.CurrentSolution.FilePath 15 | ;; 16 | 17 | let slnFileInfo = (SolutionFileInfo.load testSlnPath) 18 | slnFileInfo.Path 19 | ;; 20 | let slnInfo = SolutionFileInfo.toSolutionInfo fswork slnFileInfo 21 | 22 | let sln = fswork.AddSolutionInfo slnInfo 23 | ;; 24 | string fswork.CurrentSolution.FilePath 25 | 26 | fswork.CurrentSolution.Projects |> Seq.iter(fun p-> 27 | let docs = p.Documents |> Seq.map (fun p -> " - " + p.Name) |> String.concat "\n" 28 | printfn "%s\n%s" p.Name docs) 29 | ;; 30 | fswork.CurrentSolution.Projects 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProtoWorkspace 2 | 3 | An implementation of a Roslyn workspace to serve as the foundation for a xplat Language Service and editor tools in FSharp.Editing 4 | 5 | ## Roslyn Architecture 6 | 7 | There is often confusion about what Roslyn is and why it would be used with F#. This project has 8 | no relation to the Roslyn the C# Compiler. ProtoWorkspace is implementing a Roslyn Workspace from the 9 | Microsoft.CodeAnalysis API to take advantage of its capabilites for managing projects, solutions, document tracking, dirty buffers, and its infrastructure for implmenting editor tooling features (e.g. intellisense, refactoring, code fixes). 10 | The workspace for F# needs to be built from the ground up as the existing Roslyn workspaces are incompatible with F#. 11 | 12 | ![](https://github.com/dotnet/roslyn/wiki/images/alex-api-layers.png) 13 | 14 | For the F# workspace the compiler layer is fulfilled by the **FSharp.Compiler.Service** 15 | 16 | 17 | 18 | ## Workspace Architecture 19 | 20 | ![](https://github.com/dotnet/roslyn/wiki/images/workspace-obj-relations.png) 21 | 22 | Host Environment - 23 | Event System - 24 | SourceText Change sets - 25 | FSharp Language service integration - 26 | 27 | 28 | **[For examples of workspace implmentations see the links in Reference.md](reference.md)** -------------------------------------------------------------------------------- /data/TestSln.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Library1", "projects\Library1\Library1.fsproj", "{DD0F9644-2BEB-4FB2-8D90-C49E300CF5DC}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Library2", "projects\Library2\Library2.fsproj", "{32E0CD53-3E68-49EC-A53A-3BA62A561AED}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {DD0F9644-2BEB-4FB2-8D90-C49E300CF5DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {DD0F9644-2BEB-4FB2-8D90-C49E300CF5DC}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {DD0F9644-2BEB-4FB2-8D90-C49E300CF5DC}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {DD0F9644-2BEB-4FB2-8D90-C49E300CF5DC}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {32E0CD53-3E68-49EC-A53A-3BA62A561AED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {32E0CD53-3E68-49EC-A53A-3BA62A561AED}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {32E0CD53-3E68-49EC-A53A-3BA62A561AED}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {32E0CD53-3E68-49EC-A53A-3BA62A561AED}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /data/module_001.fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsharp-editing/ProtoWorkspace/4e2509a5da4789ccd673328dbb8ffec3b0801b8a/data/module_001.fs -------------------------------------------------------------------------------- /data/module_002.fs: -------------------------------------------------------------------------------- 1 | module Proto 2 | 3 | open System 4 | open System.IO 5 | open System.Text -------------------------------------------------------------------------------- /data/projects/Library1/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Library1.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [] 37 | [] 38 | [] 39 | 40 | do 41 | () -------------------------------------------------------------------------------- /data/projects/Library1/Library1.fs: -------------------------------------------------------------------------------- 1 | namespace Library1 2 | 3 | type Class1() = 4 | member this.X = "F#" 5 | -------------------------------------------------------------------------------- /data/projects/Library1/Library1.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | dd0f9644-2beb-4fb2-8d90-c49e300cf5dc 9 | Library 10 | Library1 11 | Library1 12 | v4.5.2 13 | 4.4.0.0 14 | true 15 | Library1 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\Library1.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\Library1.XML 35 | 36 | 37 | 38 | 39 | True 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 11 52 | 53 | 54 | 55 | 56 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 57 | 58 | 59 | 60 | 61 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 62 | 63 | 64 | 65 | 66 | 73 | -------------------------------------------------------------------------------- /data/projects/Library1/Script.fsx: -------------------------------------------------------------------------------- 1 | // Learn more about F# at http://fsharp.org. See the 'F# Tutorial' project 2 | // for more guidance on F# programming. 3 | 4 | #load "Library1.fs" 5 | open Library1 6 | 7 | // Define your library scripting code here 8 | 9 | -------------------------------------------------------------------------------- /data/projects/Library2/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Library2.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | [] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | [] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [] 37 | [] 38 | [] 39 | 40 | do 41 | () -------------------------------------------------------------------------------- /data/projects/Library2/Library1.fs: -------------------------------------------------------------------------------- 1 | namespace Library2 2 | 3 | type Class1() = 4 | member this.X = "F#" 5 | -------------------------------------------------------------------------------- /data/projects/Library2/Library2.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 32e0cd53-3e68-49ec-a53a-3ba62a561aed 9 | Library 10 | Library2 11 | Library2 12 | v4.5.2 13 | 4.4.0.0 14 | true 15 | Library2 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\Library2.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\Library2.XML 35 | 36 | 37 | 38 | 39 | True 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 11 52 | 53 | 54 | 55 | 56 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 57 | 58 | 59 | 60 | 61 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 62 | 63 | 64 | 65 | 66 | 73 | -------------------------------------------------------------------------------- /data/projects/Library2/Script.fsx: -------------------------------------------------------------------------------- 1 | // Learn more about F# at http://fsharp.org. See the 'F# Tutorial' project 2 | // for more guidance on F# programming. 3 | 4 | #load "Library1.fs" 5 | open Library2 6 | 7 | // Define your library scripting code here 8 | 9 | -------------------------------------------------------------------------------- /data/script_001.fsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | let fn x y = x * y 5 | 6 | 7 | type rekt = 8 | { a : int 9 | b : int 10 | c : int 11 | } 12 | member __.X = "" -------------------------------------------------------------------------------- /data/script_002.fsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsharp-editing/ProtoWorkspace/4e2509a5da4789ccd673328dbb8ffec3b0801b8a/data/script_002.fsx -------------------------------------------------------------------------------- /data/sig_001.fsi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsharp-editing/ProtoWorkspace/4e2509a5da4789ccd673328dbb8ffec3b0801b8a/data/sig_001.fsi -------------------------------------------------------------------------------- /data/sig_002.fsi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsharp-editing/ProtoWorkspace/4e2509a5da4789ccd673328dbb8ffec3b0801b8a/data/sig_002.fsi -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://www.nuget.org/api/v2 2 | framework: >= net45 3 | redirects: on 4 | 5 | nuget FsCheck 6 | nuget FSharp.Compiler.Service 7 | nuget FSharp.Core 8 | nuget FSharpVSPowerTools.Core 9 | nuget Microsoft.CodeAnalysis.Analyzers 10 | nuget Microsoft.CodeAnalysis.Workspaces.Common 11 | nuget Microsoft.Extensions.Caching.Memory 12 | nuget Microsoft.Extensions.Configuration 13 | nuget Microsoft.Extensions.FileSystemGlobbing 14 | nuget Microsoft.Extensions.Logging 15 | nuget Microsoft.Extensions.Logging.Console 16 | nuget Microsoft.Extensions.PlatformAbstractions 17 | nuget Newtonsoft.Json 18 | nuget NUnit 19 | nuget NUnit.Runners 20 | nuget System.Collections.Immutable 21 | nuget System.IO.FileSystem.Watcher 22 | nuget System.Threading.Thread 23 | nuget Microsoft.Build 24 | nuget Microsoft.Build.Framework 25 | nuget Microsoft.Build.Tasks.Core 26 | nuget Microsoft.Build.Utilities.Core -------------------------------------------------------------------------------- /paket.lock: -------------------------------------------------------------------------------- 1 | REDIRECTS: ON 2 | FRAMEWORK: >= NET45 3 | NUGET 4 | remote: https://www.nuget.org/api/v2 5 | FsCheck (2.6.2) 6 | FSharp.Core (>= 3.1.2.5) 7 | FSharp.Compiler.Service (8.0) 8 | System.Collections.Immutable (>= 1.2) 9 | System.Reflection.Metadata (>= 1.4.1-beta-24227-04) 10 | FSharp.Core (4.0.0.1) 11 | FSharpVSPowerTools.Core (2.4) 12 | FSharp.Compiler.Service (>= 2.0.0.8) 13 | Microsoft.Build (14.3) 14 | Microsoft.Build.Framework (14.3) 15 | Microsoft.Build.Framework (14.3) 16 | System.Collections (>= 4.0.11) - framework: >= net46 17 | System.Runtime (>= 4.1) - framework: >= net46 18 | System.Runtime.InteropServices (>= 4.1) - framework: >= net46 19 | Microsoft.Build.Tasks.Core (14.3) 20 | Microsoft.Build.Framework (14.3) 21 | Microsoft.Build.Utilities.Core (14.3) 22 | Microsoft.Build.Utilities.Core (14.3) 23 | Microsoft.Build.Framework (14.3) 24 | Microsoft.CodeAnalysis.Analyzers (1.1) 25 | Microsoft.CodeAnalysis.Common (1.3.2) 26 | Microsoft.CodeAnalysis.Analyzers (>= 1.1) 27 | System.AppContext (>= 4.1) - framework: >= net46 28 | System.Collections (>= 4.0.11) - framework: >= net46 29 | System.Collections.Concurrent (>= 4.0.12) - framework: >= net46 30 | System.Collections.Immutable (>= 1.1.37) 31 | System.Collections.Immutable (>= 1.2) - framework: >= net46 32 | System.Console (>= 4.0) - framework: >= net46 33 | System.Diagnostics.Debug (>= 4.0.11) - framework: >= net46 34 | System.Diagnostics.FileVersionInfo (>= 4.0) - framework: >= net46 35 | System.Diagnostics.StackTrace (>= 4.0.1) - framework: >= net46 36 | System.Diagnostics.Tools (>= 4.0.1) - framework: >= net46 37 | System.Dynamic.Runtime (>= 4.0.11) - framework: >= net46 38 | System.Globalization (>= 4.0.11) - framework: >= net46 39 | System.IO.FileSystem (>= 4.0.1) - framework: >= net46 40 | System.IO.FileSystem.Primitives (>= 4.0.1) - framework: >= net46 41 | System.Linq (>= 4.1) - framework: >= net46 42 | System.Linq.Expressions (>= 4.1) - framework: >= net46 43 | System.Reflection (>= 4.1) - framework: >= net46 44 | System.Reflection.Metadata (>= 1.2) 45 | System.Reflection.Metadata (>= 1.3) - framework: >= net46 46 | System.Reflection.Primitives (>= 4.0.1) - framework: >= net46 47 | System.Resources.ResourceManager (>= 4.0.1) - framework: >= net46 48 | System.Runtime (>= 4.1) - framework: >= net46 49 | System.Runtime.Extensions (>= 4.1) - framework: >= net46 50 | System.Runtime.Handles (>= 4.0.1) - framework: >= net46 51 | System.Runtime.InteropServices (>= 4.1) - framework: >= net46 52 | System.Runtime.Numerics (>= 4.0.1) - framework: >= net46 53 | System.Security.Cryptography.Algorithms (>= 4.2) - framework: >= net46 54 | System.Security.Cryptography.Encoding (>= 4.0) - framework: >= net46 55 | System.Security.Cryptography.X509Certificates (>= 4.1) - framework: >= net46 56 | System.Text.Encoding (>= 4.0.11) - framework: >= net46 57 | System.Text.Encoding.CodePages (>= 4.0.1) - framework: >= net46 58 | System.Text.Encoding.Extensions (>= 4.0.11) - framework: >= net46 59 | System.Threading (>= 4.0.11) - framework: >= net46 60 | System.Threading.Tasks (>= 4.0.11) - framework: >= net46 61 | System.Threading.Tasks.Parallel (>= 4.0.1) - framework: >= net46 62 | System.Threading.Thread (>= 4.0) - framework: >= net46 63 | System.Xml.ReaderWriter (>= 4.0.11) - framework: >= net46 64 | System.Xml.XDocument (>= 4.0.11) - framework: >= net46 65 | System.Xml.XmlDocument (>= 4.0.1) - framework: >= net46 66 | System.Xml.XPath.XDocument (>= 4.0.1) - framework: >= net46 67 | Microsoft.CodeAnalysis.Workspaces.Common (1.3.2) 68 | Microsoft.CodeAnalysis.Common (1.3.2) 69 | Microsoft.Composition (>= 1.0.27) 70 | Microsoft.Composition (1.0.30) 71 | Microsoft.Extensions.Caching.Abstractions (1.0) - framework: >= net451 72 | Microsoft.Extensions.Caching.Memory (1.0) 73 | Microsoft.Extensions.Caching.Abstractions (>= 1.0) - framework: >= net451 74 | Microsoft.Extensions.DependencyInjection.Abstractions (>= 1.0) - framework: >= net451 75 | Microsoft.Extensions.Options (>= 1.0) - framework: >= net451 76 | Microsoft.Extensions.Configuration (1.0) 77 | Microsoft.Extensions.Configuration.Abstractions (>= 1.0) 78 | System.Collections (>= 4.0.11) 79 | System.Diagnostics.Debug (>= 4.0.11) 80 | System.IO (>= 4.1) 81 | System.Resources.ResourceManager (>= 4.0.1) 82 | System.Runtime.Extensions (>= 4.1) 83 | System.Runtime.InteropServices (>= 4.1) 84 | System.Threading (>= 4.0.11) 85 | Microsoft.Extensions.Configuration.Abstractions (1.0) 86 | Microsoft.Extensions.DependencyInjection.Abstractions (1.0) 87 | Microsoft.Extensions.FileSystemGlobbing (1.0) 88 | Microsoft.Extensions.Logging (1.0) 89 | Microsoft.Extensions.DependencyInjection.Abstractions (>= 1.0) 90 | Microsoft.Extensions.Logging.Abstractions (>= 1.0) 91 | System.Threading (>= 4.0.11) 92 | Microsoft.Extensions.Logging.Abstractions (1.0) 93 | System.Collections (>= 4.0.11) 94 | System.Collections.Concurrent (>= 4.0.12) 95 | System.Diagnostics.Debug (>= 4.0.11) 96 | System.Globalization (>= 4.0.11) 97 | System.Linq (>= 4.1) 98 | System.Reflection (>= 4.1) 99 | System.Resources.ResourceManager (>= 4.0.1) 100 | System.Runtime.Extensions (>= 4.1) 101 | System.Runtime.InteropServices (>= 4.1) 102 | Microsoft.Extensions.Logging.Console (1.0) 103 | Microsoft.Extensions.Configuration.Abstractions (>= 1.0) - framework: >= net451 104 | Microsoft.Extensions.Logging.Abstractions (>= 1.0) - framework: >= net451 105 | System.Runtime.InteropServices.RuntimeInformation (>= 4.0) - framework: >= net451 106 | Microsoft.Extensions.Options (1.0) - framework: >= net451 107 | Microsoft.Extensions.PlatformAbstractions (1.0) 108 | Newtonsoft.Json (9.0.1) 109 | NUnit (3.5) 110 | NUnit.ConsoleRunner (3.5) 111 | NUnit.Extension.NUnitProjectLoader (3.5) 112 | NUnit.Extension.NUnitV2Driver (3.5) 113 | NUnit.Extension.NUnitV2ResultWriter (3.5) 114 | NUnit.Extension.TeamCityEventListener (1.0.2) 115 | NUnit.Extension.VSProjectLoader (3.5) 116 | NUnit.Runners (3.5) 117 | NUnit.ConsoleRunner (>= 3.5) 118 | NUnit.Extension.NUnitProjectLoader (>= 3.5) 119 | NUnit.Extension.NUnitV2Driver (>= 3.5) 120 | NUnit.Extension.NUnitV2ResultWriter (>= 3.5) 121 | NUnit.Extension.TeamCityEventListener (>= 1.0.2) 122 | NUnit.Extension.VSProjectLoader (>= 3.5) 123 | System.AppContext (4.1) - framework: >= net46 124 | System.Collections (4.0.11) 125 | System.Collections.Concurrent (4.0.12) 126 | System.Collections.Immutable (1.2) 127 | System.Console (4.0) - framework: >= net46 128 | System.Diagnostics.Debug (4.0.11) 129 | System.Diagnostics.FileVersionInfo (4.0) - framework: >= net46 130 | System.Diagnostics.StackTrace (4.0.1) - framework: >= net46 131 | System.Diagnostics.Tools (4.0.1) - framework: >= net46 132 | System.Dynamic.Runtime (4.0.11) - framework: >= net46 133 | System.Globalization (4.0.11) 134 | System.IO (4.1) 135 | System.IO.FileSystem (4.0.1) - framework: >= net46 136 | System.IO.FileSystem.Primitives (>= 4.0.1) - framework: >= net46 137 | System.IO.FileSystem.Primitives (4.0.1) 138 | System.IO.FileSystem.Watcher (4.0) 139 | System.Linq (4.1) 140 | System.Linq.Expressions (4.1) - framework: >= net46 141 | System.Reflection (4.1) 142 | System.Reflection.Metadata (1.4.1-beta-24430-01) 143 | System.Collections.Immutable (>= 1.2) 144 | System.Reflection.Primitives (4.0.1) - framework: >= net46 145 | System.Resources.ResourceManager (4.0.1) 146 | System.Runtime (4.1) 147 | System.Runtime.Extensions (4.1) 148 | System.Runtime.Handles (4.0.1) - framework: >= net46 149 | System.Runtime.InteropServices (4.1) 150 | System.Runtime (>= 4.1) - framework: >= net462 151 | System.Runtime.InteropServices.RuntimeInformation (4.0) - framework: >= net451 152 | System.Runtime.Numerics (4.0.1) - framework: >= net46 153 | System.Security.Cryptography.Algorithms (4.2) - framework: >= net46 154 | System.IO (>= 4.1) - framework: >= net463 155 | System.Runtime (>= 4.1) - framework: >= net463 156 | System.Security.Cryptography.Encoding (>= 4.0) - framework: >= net463 157 | System.Security.Cryptography.Primitives (>= 4.0) - framework: net46, net461, >= net463 158 | System.Security.Cryptography.Encoding (4.0) 159 | System.Security.Cryptography.Primitives (4.0) - framework: net46, net461, >= net463 160 | System.Security.Cryptography.X509Certificates (4.1) - framework: >= net46 161 | System.Security.Cryptography.Algorithms (>= 4.2) - framework: >= net46 162 | System.Security.Cryptography.Encoding (>= 4.0) - framework: >= net46 163 | System.Text.Encoding (4.0.11) - framework: >= net46 164 | System.Text.Encoding.CodePages (4.0.1) - framework: >= net46 165 | System.Text.Encoding.Extensions (4.0.11) - framework: >= net46 166 | System.Threading (4.0.11) 167 | System.Threading.Tasks (4.0.11) - framework: >= net46 168 | System.Threading.Tasks.Parallel (4.0.1) - framework: >= net46 169 | System.Threading.Thread (4.0) 170 | System.Xml.ReaderWriter (4.0.11) - framework: >= net46 171 | System.Xml.XDocument (4.0.11) - framework: >= net46 172 | System.Xml.XmlDocument (4.0.1) - framework: >= net46 173 | System.Xml.XPath (4.0.1) - framework: >= net46 174 | System.Xml.XPath.XDocument (4.0.1) - framework: >= net46 175 | System.Xml.XPath (>= 4.0.1) - framework: >= net46 176 | -------------------------------------------------------------------------------- /reference.md: -------------------------------------------------------------------------------- 1 | ## Reference Materials 2 | 3 | * [Roslyn Overview - Roslyn Wiki](https://github.com/dotnet/roslyn/wiki/Roslyn-Overview) 4 | 5 | * [Project Json Workspace implementation](https://github.com/davidfowl/ProjectJsonWorkspace/blob/master/src/Microsoft.CodeAnalysis.Workspaces.Dnx/ProjectJsonWorkspace.cs) 6 | 7 | * [RoslynPad Workspace implementation](https://github.com/aelij/RoslynPad/blob/master/src/RoslynPad.Roslyn/RoslynWorkspace.cs) 8 | 9 | * [VSEmbed - EditorWorkspace implementation](https://github.com/SLaks/VSEmbed/blob/master/VSEmbed.Roslyn/EditorWorkspace.cs) 10 | 11 | * [Learn Roslyn Now - Working with Workspaces article](https://joshvarty.wordpress.com/2014/09/12/learn-roslyn-now-part-6-working-with-workspaces/) 12 | 13 | * [Omnisharp-Roslyn - OmnisharpWorkspace implementation](https://github.com/OmniSharp/omnisharp-roslyn/blob/master/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs) 14 | 15 | * [NoMefRoslyn.cs - example of host service dummy scaffolding](https://gist.github.com/praeclarum/953629b2f80860e54747) 16 | 17 | * [Roslyn Workspace Reference Source](http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Workspace/Workspace.cs) 18 | 19 | * [FSharp.Editor - F# vsintegration of roslyn language services into the VisualStudio Workspace](https://github.com/Microsoft/visualfsharp/tree/master/vsintegration/src/FSharp.Editor) 20 | 21 | 22 | ## Misc. 23 | 24 | using MSBuild tools from - https://dotnet.myget.org/gallery/dotnet-buildtools/ 25 | 26 | MSBuild Nuget Packages (paket form) - 27 | 28 | nuget Microsoft.Build 15.1.262-preview5 29 | nuget Microsoft.Build 15.1.262-preview5 30 | nuget Microsoft.Build.Framework 15.1.262-preview5 31 | nuget Microsoft.Build.Tasks.Core 15.1.262-preview5 32 | nuget Microsoft.Build.Utilities.Core 15.1.262-preview5 33 | 34 | 35 | --------------------------------------------------------------------------------