diff --git a/paket.dependencies b/paket.dependencies
index 27dfc2973..6fd2c8b24 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -10,7 +10,6 @@ storage: none
github TheAngryByrd/FsLibLog:f81cba440bf0476bb4e2262b57a067a0d6ab78a7 src/FsLibLog/FsLibLog.fs
-nuget Argu
nuget Fantomas.Client
nuget FSharp.Compiler.Service
nuget Ionide.ProjInfo
@@ -27,7 +26,7 @@ nuget Mono.Cecil >= 0.10.0-beta7
nuget Newtonsoft.Json
# nuget Fake.Runtime prerelease
nuget FSharpLint.Core
-nuget FSharp.Core
+nuget FSharp.Core content: none
nuget Dapper
nuget Microsoft.Data.Sqlite 2.2.4
nuget Microsoft.Data.Sqlite.Core 2.2.4
@@ -44,6 +43,7 @@ nuget FSharp.Formatting
nuget FsToolkit.ErrorHandling
nuget FSharpx.Async
nuget CliWrap
+nuget System.CommandLine prerelease
nuget Microsoft.NET.Test.Sdk
nuget Dotnet.ReproducibleBuilds copy_local:true
diff --git a/paket.lock b/paket.lock
index 50a1e2a1c..5b154c200 100644
--- a/paket.lock
+++ b/paket.lock
@@ -3,9 +3,6 @@ RESTRICTION: || (== net6.0) (== netstandard2.0)
NUGET
remote: https://api.nuget.org/v3/index.json
altcover (8.2.837)
- Argu (6.1.1)
- FSharp.Core (>= 4.3.2)
- System.Configuration.ConfigurationManager (>= 4.4)
CliWrap (3.4.2)
Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0)
System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0)
@@ -67,7 +64,7 @@ NUGET
FSharp.Control.Reactive (5.0.2) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0))
FSharp.Core (>= 4.7.2)
System.Reactive (>= 5.0)
- FSharp.Core (6.0.3)
+ FSharp.Core (6.0.3) - content: none
FSharp.Formatting (15.0)
FSharp.Compiler.Service (41.0.3) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netstandard2.1))
FSharp.UMX (1.1)
@@ -92,7 +89,8 @@ NUGET
FSharp.Core (>= 4.7.2)
GitHubActionsTestLogger (1.3)
Microsoft.TestPlatform.ObjectModel (>= 17.0)
- ICSharpCode.Decompiler (7.2.0.6844)
+ ICSharpCode.Decompiler (7.2.1.6856)
+ Microsoft.Win32.Registry (>= 5.0)
System.Collections.Immutable (>= 5.0)
System.Reflection.Metadata (>= 5.0)
Ionide.KeepAChangelog.Tasks (0.1.6) - copy_local: true
@@ -224,7 +222,9 @@ NUGET
Microsoft.NETCore.Platforms (>= 1.1)
Microsoft.NETCore.Targets (>= 1.1)
System.Runtime (>= 4.3)
- Microsoft.Win32.Registry (5.0) - copy_local: false
+ Microsoft.Win32.Registry (5.0)
+ System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= monoandroid) (< netstandard1.3)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (== netstandard2.0)
+ System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0)
System.Security.AccessControl (>= 5.0)
System.Security.Principal.Windows (>= 5.0)
Microsoft.Win32.SystemEvents (6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp3.1))
@@ -340,6 +340,8 @@ NUGET
System.Collections.Immutable (6.0)
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (== netstandard2.0)
System.Runtime.CompilerServices.Unsafe (>= 6.0)
+ System.CommandLine (2.0.0-beta3.22114.1)
+ System.Memory (>= 4.5.4) - restriction: == netstandard2.0
System.Configuration.ConfigurationManager (6.0)
System.Security.Cryptography.ProtectedData (>= 6.0)
System.Security.Permissions (>= 6.0)
@@ -550,7 +552,7 @@ NUGET
System.Resources.ResourceManager (>= 4.3)
System.Runtime (>= 4.3)
System.Threading.Tasks (>= 4.3)
- System.Numerics.Vectors (4.5) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0)
+ System.Numerics.Vectors (4.5) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (== netstandard2.0)
System.ObjectModel (4.3)
System.Collections (>= 4.3)
System.Diagnostics.Debug (>= 4.3)
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 913ad3ed4..0c92968a9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -5,5 +5,7 @@
true
$(NoWarn);FS2003
+
+ $(NoWarn);NU5104
diff --git a/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj b/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj
index 8bd947cb3..aa46eab79 100644
--- a/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj
+++ b/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj
@@ -5,6 +5,7 @@
+
diff --git a/src/FsAutoComplete/FsAutoComplete.fsproj b/src/FsAutoComplete/FsAutoComplete.fsproj
index 9968bfd24..3994a9f4f 100644
--- a/src/FsAutoComplete/FsAutoComplete.fsproj
+++ b/src/FsAutoComplete/FsAutoComplete.fsproj
@@ -12,18 +12,17 @@
logo.png
$(RepositoryUrl)
FsAutoComplete contributors
- false
-
+
diff --git a/src/FsAutoComplete/Options.fs b/src/FsAutoComplete/Options.fs
deleted file mode 100644
index dd098d6af..000000000
--- a/src/FsAutoComplete/Options.fs
+++ /dev/null
@@ -1,85 +0,0 @@
-// --------------------------------------------------------------------------------------
-// (c) Robin Neatherway
-// --------------------------------------------------------------------------------------
-namespace FsAutoComplete
-
-open System
-open Serilog
-open Serilog.Core
-open Serilog.Events
-
-module Options =
- open Argu
-
- type CLIArguments =
- | Version
- | [] Verbose
- | AttachDebugger
- | [] Logfile of path:string
- | Filter of filter: string list
- | [] WaitForDebugger
- | [] HostPID of pid:int
- | [] BackgroundServiceEnabled
- | [] ProjectGraphEnabled
- with
- interface IArgParserTemplate with
- member s.Usage =
- match s with
- | Version -> "display versioning information"
- | AttachDebugger -> "launch the system debugger and break."
- | Verbose -> "enable verbose mode"
- | Logfile _ -> "send verbose output to specified log file"
- | Filter _ -> "filter out messages that match the provided category"
- | WaitForDebugger _ -> "wait for a debugger to attach to the process"
- | HostPID _ -> "the Host process ID."
- | BackgroundServiceEnabled -> "enable background service"
- | ProjectGraphEnabled -> "enable MsBuild ProjectGraph for workspace loading. Experimental."
-
- let isCategory (category: string) (e: LogEvent) =
- match e.Properties.TryGetValue "SourceContext" with
- | true, loggerName ->
- match loggerName with
- | :? ScalarValue as v ->
- match v.Value with
- | :? string as s when s = category -> true
- | _ -> false
- | _ -> false
- | false, _ -> false
-
- let hasMinLevel (minLevel: LogEventLevel) (e: LogEvent) =
- e.Level >= minLevel
-
- // will use later when a mapping-style config of { "category": "minLevel" } is established
- let excludeByLevelWhenCategory category level event = isCategory category event || not (hasMinLevel level event)
-
- let apply (levelSwitch: LoggingLevelSwitch) (logConfig: Serilog.LoggerConfiguration) (args: ParseResults) =
-
- let applyArg arg =
- match arg with
- | Verbose ->
- levelSwitch.MinimumLevel <- LogEventLevel.Verbose
- ()
- | AttachDebugger ->
- System.Diagnostics.Debugger.Launch() |> ignore
- | Logfile s ->
- try
- logConfig.WriteTo.Async(fun c -> c.File(path = s, levelSwitch = levelSwitch) |> ignore) |> ignore
- with
- | e ->
- eprintfn "Bad log file: %s" e.Message
- exit 1
- | Filter categories ->
- categories
- |> List.iter (fun category ->
- // category is encoded in the SourceContext property, so we filter messages based on that property's value
- logConfig.Filter.ByExcluding(Func<_,_>(isCategory category)) |> ignore
- )
- | Version
- | WaitForDebugger
- | BackgroundServiceEnabled
- | ProjectGraphEnabled
- | HostPID _ ->
- ()
-
- args.GetAllResults()
- |> List.iter applyArg
diff --git a/src/FsAutoComplete/Parser.fs b/src/FsAutoComplete/Parser.fs
new file mode 100644
index 000000000..a880b3f71
--- /dev/null
+++ b/src/FsAutoComplete/Parser.fs
@@ -0,0 +1,244 @@
+namespace FsAutoComplete
+
+open System
+open Serilog
+open Serilog.Core
+open Serilog.Events
+open System.CommandLine
+open System.CommandLine.Parsing
+open System.CommandLine.Builder
+open Serilog.Filters
+
+module Parser =
+ open System.Threading.Tasks
+ []
+ type Pos = { Line: int; Column: int }
+
+ []
+ type Rng =
+ { File: string
+ Start: FSharp.Compiler.Text.pos
+ End: FSharp.Compiler.Text.pos }
+
+ let private setArity arity (o: #Option) =
+ o.Arity <- arity
+ o
+
+ /// set option to expect no arguments (e.g a flag-style argument: `--verbose`)
+ let inline private zero x = setArity ArgumentArity.Zero x
+ /// set option to expect one argument (e.g a single value: `--foo bar)
+ let inline private one x = setArity ArgumentArity.ExactlyOne x
+
+ /// set option to expect multiple arguments
+ /// (e.g a list of values: `--foo bar baz` or `--foo bar --foo baz` depending on the style)
+ let inline private many x = setArity ArgumentArity.OneOrMore x
+
+ /// set option to allow multiple arguments per use of the option flag
+ /// (e.g. `--foo bar baz` is equivalent to `--foo bar --foo baz`)
+ let inline private multipleArgs (x: #Option) =
+ x.AllowMultipleArgumentsPerToken <- true
+ x
+
+ let verboseOption =
+ Option([| "--verbose"; "-v"; "--debug" |], "Enable verbose logging. This is equivalent to --log-level debug.")
+ |> setArity ArgumentArity.Zero
+
+ let logLevelOption =
+ Option("--log-level", "Set the log verbosity to a specific level.")
+
+ let attachOption =
+ Option("--attach-debugger", "Launch the system debugger and break immediately")
+ |> zero
+
+ let logFileOption =
+ Option([| "--logfile"; "-l"; "--log-file" |], "Send log output to specified file.")
+ |> one
+
+ let logFilterOption =
+ Option(
+ [| "--filter"; "--log-filter" |],
+ "Filter logs by category. The category can be seen in the logs inside []. For example: [Compiler]."
+ )
+ |> many
+ |> fun o ->
+ o.AllowMultipleArgumentsPerToken <- true
+ o
+
+ let waitForDebuggerOption =
+ Option(
+ "--wait-for-debugger",
+ "Stop execution on startup until an external debugger to attach to this process"
+ )
+ |> zero
+
+ let backgroundServiceOption =
+ Option(
+ "--background-service-enabled",
+ "Enable running typechecking services in a background process. Enables various performance optimizations."
+ )
+ |> zero
+
+ let projectGraphOption =
+ Option(
+ "--project-graph-enabled",
+ "Enable MSBuild Graph workspace loading. Should be faster than the default, but is experimental."
+ )
+ |> zero
+
+ let rootCommand =
+ let rootCommand = RootCommand("An F# LSP server implementation")
+
+ rootCommand.AddOption verboseOption
+ rootCommand.AddOption attachOption
+ rootCommand.AddOption logFileOption
+ rootCommand.AddOption logFilterOption
+ rootCommand.AddOption waitForDebuggerOption
+ rootCommand.AddOption backgroundServiceOption
+ rootCommand.AddOption projectGraphOption
+ rootCommand.AddOption logLevelOption
+ rootCommand.SetHandler(
+ Func<_,_,Task>(fun backgroundServiceEnabled projectGraphEnabled ->
+ let workspaceLoaderFactory =
+ if projectGraphEnabled then Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create
+ else Ionide.ProjInfo.WorkspaceLoader.Create
+
+ let toolsPath = Ionide.ProjInfo.Init.init (IO.DirectoryInfo Environment.CurrentDirectory) None
+ use _compilerEventListener = new Debug.FSharpCompilerEventLogger.Listener()
+ let result = Lsp.start backgroundServiceEnabled toolsPath workspaceLoaderFactory
+
+ Task.FromResult result
+ ), backgroundServiceOption, projectGraphOption)
+ rootCommand
+
+ let waitForDebugger =
+ Invocation.InvocationMiddleware (fun ctx next ->
+ let waitForDebugger = ctx.ParseResult.HasOption waitForDebuggerOption
+
+ if waitForDebugger then
+ Debug.waitForDebugger ()
+
+ next.Invoke(ctx))
+
+ let immediateAttach =
+ Invocation.InvocationMiddleware (fun ctx next ->
+ let attachDebugger = ctx.ParseResult.HasOption attachOption
+
+ if attachDebugger then
+ Diagnostics.Debugger.Launch()
+ |> ignore
+
+ next.Invoke(ctx))
+
+ let configureLogging =
+ Invocation.InvocationMiddleware (fun ctx next ->
+ let isCategory (category: string) (e: LogEvent) =
+ match e.Properties.TryGetValue "SourceContext" with
+ | true, loggerName ->
+ match loggerName with
+ | :? ScalarValue as v ->
+ match v.Value with
+ | :? string as s when s = category -> true
+ | _ -> false
+ | _ -> false
+ | false, _ -> false
+
+ let hasMinLevel (minLevel: LogEventLevel) (e: LogEvent) = e.Level >= minLevel
+
+ // will use later when a mapping-style config of { "category": "minLevel" } is established
+ let excludeByLevelWhenCategory category level event =
+ isCategory category event
+ || not (hasMinLevel level event)
+
+ let args = ctx.ParseResult
+
+ let logLevel =
+ if args.HasOption verboseOption then
+ LogEventLevel.Debug
+ else if args.HasOption logLevelOption then
+ args.GetValueForOption logLevelOption
+ else
+ LogEventLevel.Warning
+
+ let logSourcesToExclude =
+ if args.HasOption logFilterOption then
+ args.GetValueForOption logFilterOption
+ else
+ [||]
+
+ let sourcesToExclude =
+ Matching.WithProperty(
+ Constants.SourceContextPropertyName,
+ fun s -> s <> null && Array.contains s logSourcesToExclude
+ )
+
+ let verbositySwitch = LoggingLevelSwitch(logLevel)
+
+ let outputTemplate =
+ "[{Timestamp:HH:mm:ss.fff} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
+
+ let logConf =
+ LoggerConfiguration()
+ .MinimumLevel.ControlledBy(verbositySwitch)
+ .Filter.ByExcluding(Matching.FromSource("FileSystem"))
+ .Filter.ByExcluding(sourcesToExclude)
+ .Enrich.FromLogContext()
+ .Destructure.FSharpTypes()
+ .Destructure
+ .ByTransforming(fun r ->
+ { File = r.FileName
+ Start = r.Start
+ End = r.End })
+ .Destructure.ByTransforming(fun r -> { Line = r.Line; Column = r.Column })
+ .Destructure.ByTransforming(fun tok -> tok.ToString() |> box)
+ .Destructure.ByTransforming(fun di -> box di.FullName)
+ .WriteTo
+ .Async(fun c ->
+ c.Console(
+ outputTemplate = outputTemplate,
+ standardErrorFromLevel = Nullable<_>(LogEventLevel.Verbose),
+ theme = Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code
+ )
+ |> ignore) // make it so that every console log is logged to stderr so that we don't interfere with LSP stdio
+
+ if args.HasOption logFileOption then
+ let logFile = args.GetValueForOption logFileOption
+
+ try
+ logConf.WriteTo.Async (fun c ->
+ c.File(path = logFile, levelSwitch = verbositySwitch)
+ |> ignore)
+ |> ignore
+ with
+ | e ->
+ eprintfn "Bad log file: %s" e.Message
+ exit 1
+
+ if args.HasOption logFilterOption then
+ let categories = args.GetValueForOption logFilterOption
+
+ categories
+ |> Array.iter (fun category ->
+ // category is encoded in the SourceContext property, so we filter messages based on that property's value
+ logConf.Filter.ByExcluding(Func<_, _>(isCategory category))
+ |> ignore)
+
+ let logger = logConf.CreateLogger()
+ Serilog.Log.Logger <- logger
+ Logging.LogProvider.setLoggerProvider (Logging.Providers.SerilogProvider.create ())
+ next.Invoke(ctx))
+
+ let serilogFlush =
+ Invocation.InvocationMiddleware (fun ctx next ->
+ task {
+ do! next.Invoke ctx
+ Serilog.Log.CloseAndFlush()
+ })
+
+ let parser =
+ CommandLineBuilder(rootCommand)
+ .UseDefaults()
+ .AddMiddleware(waitForDebugger)
+ .AddMiddleware(immediateAttach)
+ .AddMiddleware(serilogFlush)
+ .AddMiddleware(configureLogging)
+ .Build()
diff --git a/src/FsAutoComplete/Program.fs b/src/FsAutoComplete/Program.fs
index 9611415d9..3b28e4aeb 100644
--- a/src/FsAutoComplete/Program.fs
+++ b/src/FsAutoComplete/Program.fs
@@ -1,77 +1,8 @@
module FsAutoComplete.Program
-open System
-open FsAutoComplete.JsonSerializer
-open Argu
-open Serilog
-open Serilog.Core
-open Serilog.Events
-open FsAutoComplete.Logging
+open System.CommandLine.Parsing
[]
let entry args =
-
-
- try
- let parser = ArgumentParser.Create(programName = "fsautocomplete")
-
- let results = parser.Parse args
-
- System.Threading.ThreadPool.SetMinThreads(16, 16) |> ignore
-
- // default the verbosity to warning
- let verbositySwitch = LoggingLevelSwitch(LogEventLevel.Warning)
- let outputTemplate = "[{Timestamp:HH:mm:ss.fff} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
- let logConf =
- LoggerConfiguration()
- .MinimumLevel.ControlledBy(verbositySwitch)
- .Enrich.FromLogContext()
- .Destructure.FSharpTypes()
- .Destructure.ByTransforming(fun r -> box {| FileName = r.FileName; Start = r.Start; End = r.End |})
- .Destructure.ByTransforming(fun r -> box {| Line = r.Line; Column = r.Column |})
- .Destructure.ByTransforming(fun tok -> tok.ToString() |> box)
- .Destructure.ByTransforming(fun di -> box di.FullName)
- .WriteTo.Async(
- fun c -> c.Console(outputTemplate = outputTemplate, standardErrorFromLevel = Nullable<_>(LogEventLevel.Verbose), theme = Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code) |> ignore
- ) // make it so that every console log is logged to stderr
-
-
- results.TryGetResult(<@ Options.CLIArguments.WaitForDebugger @>)
- |> Option.iter (ignore >> Debug.waitForDebugger)
-
- results.TryGetResult(<@ Options.CLIArguments.Version @>)
- |> Option.iter (fun _ ->
- let version = Version.info ()
- printfn "FsAutoComplete %s (git sha %s)" (version.Version) (version.GitSha)
- exit 0 )
-
- Options.apply verbositySwitch logConf results
-
- let logger = logConf.CreateLogger()
- Serilog.Log.Logger <- logger
- LogProvider.setLoggerProvider (Providers.SerilogProvider.create())
-
- let backgroundServiceEnabled =
- results.Contains <@ Options.CLIArguments.BackgroundServiceEnabled @>
-
- let projectGraphEnabled =
- results.Contains <@ Options.CLIArguments.ProjectGraphEnabled @>
-
- let workspaceLoaderFactory =
- if projectGraphEnabled then Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create
- else Ionide.ProjInfo.WorkspaceLoader.Create
-
- let toolsPath = Ionide.ProjInfo.Init.init (System.IO.DirectoryInfo Environment.CurrentDirectory) None
- use _compilerEventListener = new Debug.FSharpCompilerEventLogger.Listener()
- let result = FsAutoComplete.Lsp.start backgroundServiceEnabled toolsPath workspaceLoaderFactory
- Serilog.Log.CloseAndFlush()
- result
- with
- | :? ArguParseException as ex ->
- printfn "%s" ex.Message
- match ex.ErrorCode with
- | ErrorCode.HelpText -> 0
- | _ -> 1 // Unrecognised arguments
- | e ->
- printfn "Server crashing error - %s \n %s" e.Message e.StackTrace
- 3
+ let results = Parser.parser.Invoke args
+ results
diff --git a/src/FsAutoComplete/paket.references b/src/FsAutoComplete/paket.references
index 4194033ed..f25dd059c 100644
--- a/src/FsAutoComplete/paket.references
+++ b/src/FsAutoComplete/paket.references
@@ -1,4 +1,4 @@
-Argu
+#FSharpLint.Core
CliWrap
Dapper
Destructurama.FSharp
@@ -7,9 +7,10 @@ FSharp.Analyzers.SDK
FSharp.Compiler.Service
FSharp.Core
FSharp.UMX
-#FSharpLint.Core
FsToolkit.ErrorHandling
ICSharpCode.Decompiler
+Ionide.KeepAChangelog.Tasks
+Ionide.LanguageServerProtocol
Ionide.ProjInfo
Ionide.ProjInfo.ProjectSystem
Microsoft.Data.Sqlite
@@ -20,6 +21,5 @@ Serilog
Serilog.Sinks.Async
Serilog.Sinks.Console
Serilog.Sinks.File
+System.CommandLine
System.Configuration.ConfigurationManager
-Ionide.LanguageServerProtocol
-Ionide.KeepAChangelog.Tasks