diff --git a/src/FsAutoComplete/LspHelpers.fs b/src/FsAutoComplete/LspHelpers.fs index db53f33c6..384ccb4f8 100644 --- a/src/FsAutoComplete/LspHelpers.fs +++ b/src/FsAutoComplete/LspHelpers.fs @@ -9,6 +9,8 @@ open System.Collections.Generic open Ionide.ProjInfo.ProjectSystem open FSharp.Compiler.Diagnostics open FSharp.Compiler.EditorServices +open System.Text.RegularExpressions +open FsAutoComplete.Logging type FcsRange = FSharp.Compiler.Text.Range @@ -640,8 +642,11 @@ type FSharpConfigDto = InterfaceStubGenerationMethodBody: string option AddPrivateAccessModifier: bool option UnusedOpensAnalyzer: bool option + UnusedOpensAnalyzerExclusions : string array option UnusedDeclarationsAnalyzer: bool option + UnusedDeclarationsAnalyzerExclusions : string array option SimplifyNameAnalyzer: bool option + SimplifyNameAnalyzerExclusions : string array option ResolveNamespaces: bool option EnableReferenceCodeLens: bool option EnableAnalyzers: bool option @@ -738,6 +743,19 @@ type DebugConfig = LogDurationBetweenCheckFiles = false LogCheckFileDuration = false } +let logger = lazy (LogProvider.getLoggerByName "LspHelpers") + +let tryCreateRegex (pattern: string) = + try + Some (Regex(pattern)) + with + | e -> + logger.Value.warn ( + Log.setMessageI $"Invalid regex pattern: {pattern}" + >> Log.addExn e + ) + None + type FSharpConfig = { AutomaticWorkspaceInit: bool WorkspaceModePeekDeepLevel: int @@ -759,8 +777,11 @@ type FSharpConfig = InterfaceStubGenerationMethodBody: string AddPrivateAccessModifier: bool UnusedOpensAnalyzer: bool + UnusedOpensAnalyzerExclusions: Regex array UnusedDeclarationsAnalyzer: bool + UnusedDeclarationsAnalyzerExclusions: Regex array SimplifyNameAnalyzer: bool + SimplifyNameAnalyzerExclusions: Regex array ResolveNamespaces: bool EnableReferenceCodeLens: bool EnableAnalyzers: bool @@ -801,8 +822,11 @@ type FSharpConfig = InterfaceStubGenerationMethodBody = "failwith \"Not Implemented\"" AddPrivateAccessModifier = false UnusedOpensAnalyzer = false + UnusedOpensAnalyzerExclusions = [||] UnusedDeclarationsAnalyzer = false + UnusedDeclarationsAnalyzerExclusions = [||] SimplifyNameAnalyzer = false + SimplifyNameAnalyzerExclusions = [||] ResolveNamespaces = false EnableReferenceCodeLens = false EnableAnalyzers = false @@ -841,8 +865,11 @@ type FSharpConfig = defaultArg dto.InterfaceStubGenerationMethodBody "failwith \"Not Implemented\"" AddPrivateAccessModifier = defaultArg dto.AddPrivateAccessModifier false UnusedOpensAnalyzer = defaultArg dto.UnusedOpensAnalyzer false + UnusedOpensAnalyzerExclusions = defaultArg dto.UnusedOpensAnalyzerExclusions [||] |> Array.choose tryCreateRegex UnusedDeclarationsAnalyzer = defaultArg dto.UnusedDeclarationsAnalyzer false + UnusedDeclarationsAnalyzerExclusions = defaultArg dto.UnusedDeclarationsAnalyzerExclusions [||] |> Array.choose tryCreateRegex SimplifyNameAnalyzer = defaultArg dto.SimplifyNameAnalyzer false + SimplifyNameAnalyzerExclusions = defaultArg dto.SimplifyNameAnalyzerExclusions [||] |> Array.choose tryCreateRegex ResolveNamespaces = defaultArg dto.ResolveNamespaces false EnableReferenceCodeLens = defaultArg dto.EnableReferenceCodeLens false EnableAnalyzers = defaultArg dto.EnableAnalyzers false @@ -932,8 +959,11 @@ type FSharpConfig = defaultArg dto.InterfaceStubGenerationMethodBody x.InterfaceStubGenerationMethodBody AddPrivateAccessModifier = defaultArg dto.AddPrivateAccessModifier x.AddPrivateAccessModifier UnusedOpensAnalyzer = defaultArg dto.UnusedOpensAnalyzer x.UnusedOpensAnalyzer + UnusedOpensAnalyzerExclusions = defaultArg (dto.UnusedOpensAnalyzerExclusions |> Option.map (Array.choose tryCreateRegex)) x.UnusedOpensAnalyzerExclusions UnusedDeclarationsAnalyzer = defaultArg dto.UnusedDeclarationsAnalyzer x.UnusedDeclarationsAnalyzer + UnusedDeclarationsAnalyzerExclusions = defaultArg (dto.UnusedDeclarationsAnalyzerExclusions |> Option.map (Array.choose tryCreateRegex)) x.UnusedDeclarationsAnalyzerExclusions SimplifyNameAnalyzer = defaultArg dto.SimplifyNameAnalyzer x.SimplifyNameAnalyzer + SimplifyNameAnalyzerExclusions = defaultArg (dto.SimplifyNameAnalyzerExclusions |> Option.map (Array.choose tryCreateRegex)) x.SimplifyNameAnalyzerExclusions ResolveNamespaces = defaultArg dto.ResolveNamespaces x.ResolveNamespaces EnableReferenceCodeLens = defaultArg dto.EnableReferenceCodeLens x.EnableReferenceCodeLens EnableAnalyzers = defaultArg dto.EnableAnalyzers x.EnableAnalyzers diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index bddf4f287..b478f62eb 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -40,6 +40,7 @@ open FsAutoComplete.LspHelpers open FsAutoComplete.UnionPatternMatchCaseGenerator open System.Collections.Concurrent open System.Diagnostics +open System.Text.RegularExpressions [] type WorkspaceChosen = @@ -251,16 +252,24 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let builtInCompilerAnalyzers config (file: VolatileFile) (tyRes: ParseAndCheckResults) = let filePath = file.FileName + let filePathUntag = UMX.untag filePath let source = file.Lines let version = file.Version + + let inline getSourceLine lineNo = + (source: ISourceText).GetLineString(lineNo - 1) + let checkUnusedOpens = async { try - let! ct = Async.CancellationToken + use progress = new ServerProgressReport(lspClient) + do! progress.Begin("Checking unused opens...", message = filePathUntag) let! unused = - UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, (fun i -> (source: ISourceText).GetLineString(i - 1))) + UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, getSourceLine) + + let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.UnusedOpens(filePath, (unused |> List.toArray)), ct) with e -> @@ -270,11 +279,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let checkUnusedDeclarations = async { try - let! ct = Async.CancellationToken - let isScript = Utils.isAScript (UMX.untag filePath) + use progress = new ServerProgressReport(lspClient) + do! progress.Begin("Checking unused declarations...", message = filePathUntag) + + let isScript = Utils.isAScript (filePathUntag) let! unused = UnusedDeclarations.getUnusedDeclarations (tyRes.GetCheckResults, isScript) let unused = unused |> Seq.toArray + let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.UnusedDeclarations(filePath, unused), ct) with e -> logger.error (Log.setMessage "checkUnusedDeclarations failed" >> Log.addExn e) @@ -283,26 +295,31 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let checkSimplifiedNames = async { try - let getSourceLine lineNo = - (source: ISourceText).GetLineString(lineNo - 1) + use progress = new ServerProgressReport(lspClient) + do! progress.Begin("Checking simplifing of names...", message = filePathUntag) - let! ct = Async.CancellationToken let! simplified = SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine) let simplified = Array.ofSeq simplified + let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.SimplifyNames(filePath, simplified), ct) with e -> logger.error (Log.setMessage "checkSimplifiedNames failed" >> Log.addExn e) } + let inline isNotExcluded (exclusions : Regex array) = + exclusions + |> Array.exists (fun r -> r.IsMatch filePathUntag) + |> not + let analyzers = [ // if config.Linter then // commands.Lint filePath |> Async .Ignore - if config.UnusedOpensAnalyzer then + if config.UnusedOpensAnalyzer && isNotExcluded config.UnusedOpensAnalyzerExclusions then checkUnusedOpens - if config.UnusedDeclarationsAnalyzer then + if config.UnusedDeclarationsAnalyzer && isNotExcluded config.UnusedDeclarationsAnalyzerExclusions then checkUnusedDeclarations - if config.SimplifyNameAnalyzer then + if config.SimplifyNameAnalyzer && isNotExcluded config.SimplifyNameAnalyzerExclusions then checkSimplifiedNames ] async { @@ -322,6 +339,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let file = volatileFile.FileName try + use progress = new ServerProgressReport(lspClient) + do! progress.Begin("Running analyzers...", message = UMX.untag file) Loggers.analyzers.info ( Log.setMessage "begin analysis of {file}" >> Log.addContextDestructured "file" file