-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIDE.fs
202 lines (171 loc) · 7.74 KB
/
IDE.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
module Scripts.IDE
open System.IO
open System.Text.RegularExpressions
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Diagnostics
open Ionide.ProjInfo.Types
open Scripts.Compiler
open System
open Ionide.ProjInfo
open Serilog
open Serilog.Events
type ProjectLanguage =
| FSharp
| CSharp
let projectLanguage (projectFile : string) =
let extension = Path.GetExtension(projectFile)
match extension with
| ".fsproj" -> ProjectLanguage.FSharp
| ".csproj" -> ProjectLanguage.CSharp
| s -> failwith $"Unrecognised project extension '{extension}'"
type Project =
{ Raw: ProjectOptions
FCS: FSharpProjectOptions option }
member this.Name = this.Raw.ProjectFileName
member this.ShortName = Path.GetFileNameWithoutExtension(this.Name)
member this.Language = projectLanguage this.Raw.ProjectFileName
type FSharpProjectOptions with
member x.ProjectDir = Path.GetDirectoryName(x.ProjectFileName)
member x.HasNoCSharpReferences =
x.ReferencedProjects
|> Array.forall (function | FSharpReferencedProject.FSharpReference _ -> true | _ -> false)
let pathRelativeToProjectOrSolution (op: FSharpProjectOptions) (filename: string) (solutionPath : string) =
if Object.ReferenceEquals(null, op) then
Path.GetRelativePath(solutionPath, filename)
else
Path.GetRelativePath(op.ProjectDir, filename)
let private subscribeToChecker (solutionPath : string) (checker: FSharpChecker) =
checker.FileChecked.AddHandler(fun (sender: obj) (filename: string, op: FSharpProjectOptions) ->
let name =
(if Object.ReferenceEquals(null, op) then
"-"
else
op.ProjectFileName)
|> Path.GetFileNameWithoutExtension
Log.Information("{project} | FileChecked {file}", name.PadRight(20), pathRelativeToProjectOrSolution op filename solutionPath)
())
checker.FileParsed.AddHandler(fun (sender: obj) (filename: string, op: FSharpProjectOptions) ->
let name =
(if Object.ReferenceEquals(null, op) then
"-"
else
op.ProjectFileName)
|> Path.GetFileNameWithoutExtension
Log.Information("{project} | FileParsed {file}", name.PadRight(20), pathRelativeToProjectOrSolution op filename solutionPath)
())
checker.ProjectChecked.AddHandler(fun (sender: obj) (op: FSharpProjectOptions) ->
let name =
(if Object.ReferenceEquals(null, op) then
"-"
else
op.ProjectFileName)
|> Path.GetFileNameWithoutExtension
Log.Information("{project} | ProjectChecked", name.PadRight(20))
())
type CheckerOptions =
{ UseTransparentCompiler: bool
EnablePartialTypeChecking: bool
ParallelReferenceResolution: bool }
type IDE(slnPath: string, projectFilter: string option, ?configuration: Configuration,
?checkerOptionsOverrides: CheckerOptions -> CheckerOptions,
?msbuildProps: Map<string, string>) =
let configuration = configuration |> Option.defaultValue Configuration.Debug
let slnDir = DirectoryInfo(Path.GetDirectoryName(slnPath))
let toolsPath = Init.init slnDir None
let msbuildPropsStringCommandLineString =
msbuildProps
|> Option.defaultValue Map.empty
|> Seq.map (fun (KeyValue(k, v)) -> $"/p:{k}={v}")
|> fun items -> String.Join(" ", items)
let extraProps = [ "Configuration", configuration.ToString() ]
let globalProps =
msbuildProps
|> Option.defaultValue Map.empty
|> Seq.map (fun (KeyValue(k, v)) -> k, v)
|> Seq.toList
|> List.append extraProps
let workspaceLoader = WorkspaceLoaderViaProjectGraph.Create(toolsPath, globalProps)
let mutable projects: Map<string, Project> = Map.empty
let defaultCheckerOptions =
{ ParallelReferenceResolution = true
EnablePartialTypeChecking = true
UseTransparentCompiler = true }
let checkerOptions =
checkerOptionsOverrides
|> Option.map (fun overrides -> overrides defaultCheckerOptions)
|> Option.defaultValue defaultCheckerOptions
let checker =
FSharpChecker.Create(
parallelReferenceResolution = checkerOptions.ParallelReferenceResolution,
enablePartialTypeChecking = checkerOptions.EnablePartialTypeChecking,
useTransparentCompiler = checkerOptions.UseTransparentCompiler
)
do subscribeToChecker slnDir.FullName checker
member x.RestoreSln() = Build.restoreProject slnPath msbuildPropsStringCommandLineString
member x.BuildSln() =
Build.buildProject slnPath None msbuildPropsStringCommandLineString
member x.LoadProjects() =
let unfilteredPs =
workspaceLoader.LoadSln(slnPath)
|> Seq.toArray
let ps =
unfilteredPs
|> fun ps ->
match projectFilter with
| Some filter -> ps |> Array.filter (fun p -> Regex.IsMatch(p.ProjectFileName, filter) = false)
| None -> ps
let fsharpProjects =
ps
|> Array.filter (fun p -> projectLanguage p.ProjectFileName = ProjectLanguage.FSharp)
let fcsProjects =
FCS.mapManyOptions ps
|> Seq.map (fun p -> p)
|> Seq.toArray
projects <-
Array.zip ps fcsProjects
|> Array.map (fun (p, fp) ->
let fcs =
match projectLanguage p.ProjectFileName with
| ProjectLanguage.FSharp -> Some fp
| _ -> None
p.ProjectFileName, { Project.Raw = p; Project.FCS = fcs }
)
|> Map.ofArray
let projectsString =
projects
|> Map.toArray
|> Array.map fst
|> fun projectNames -> String.Join(Environment.NewLine, projectNames)
Log.Information($"Loaded {projects.Count} projects: {Environment.NewLine}{projectsString}")
member x.Projects = projects
member x.CheckAllFSharpProjects(?inParallel : bool) =
let inParallel = inParallel |> Option.defaultValue false
projects
|> Map.toArray
|> Array.filter (fun (n, p) -> p.Language = ProjectLanguage.FSharp)
|> Seq.map (fun (n, p) ->
async {
Log.Information("ParseAndCheckProject start | {project}", Path.GetRelativePath(slnDir.FullName, p.Name))
let res = checker.ParseAndCheckProject(p.FCS.Value) |> Async.StartAsTask |> _.Result
Log.Information("ParseAndCheckProject end | {project}", Path.GetRelativePath(slnDir.FullName, p.Name))
return res
})
|> Seq.toArray
|> fun x -> if inParallel then Async.Parallel x else Async.Sequential x
|> Async.StartAsTask
|> fun t ->
for projRes in t.Result do
for d in projRes.Diagnostics do
let level =
match d.Severity with
| FSharpDiagnosticSeverity.Hidden -> LogEventLevel.Verbose
| FSharpDiagnosticSeverity.Info -> LogEventLevel.Debug
| FSharpDiagnosticSeverity.Warning -> LogEventLevel.Warning
| FSharpDiagnosticSeverity.Error -> LogEventLevel.Error
Log.Write(level,
"Diagnostic | {project} | {filename}:{range} | {message}",
Path.GetFileName(projRes.ProjectContext.ProjectOptions.ProjectFileName),
Path.GetRelativePath(slnDir.FullName, d.FileName),
$"{d.Range.StartLine}-{d.Range.EndLine}",
d.Message
)