diff --git a/src/FsAutoComplete/LspHelpers.fs b/src/FsAutoComplete/LspHelpers.fs index db53f33c6..027aaa58f 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,15 @@ 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 +773,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 +818,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 +861,15 @@ 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,21 @@ 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 4a8f96feb..0a07d5c23 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 open IcedTasks [] @@ -256,13 +257,19 @@ type AdaptiveFSharpLspServer let source = file.Source 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, getSourceLine) - let! unused = - UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, (fun i -> (source: ISourceText).GetLineString(i - 1))) + let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.UnusedOpens(filePath, (unused |> List.toArray)), ct) with e -> @@ -272,11 +279,14 @@ type AdaptiveFSharpLspServer 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) @@ -285,26 +295,36 @@ type AdaptiveFSharpLspServer 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 { @@ -324,6 +344,9 @@ type AdaptiveFSharpLspServer 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 diff --git a/test/FsAutoComplete.Tests.Lsp/Helpers.fs b/test/FsAutoComplete.Tests.Lsp/Helpers.fs index 104a5eeb3..0fa3240fd 100644 --- a/test/FsAutoComplete.Tests.Lsp/Helpers.fs +++ b/test/FsAutoComplete.Tests.Lsp/Helpers.fs @@ -236,8 +236,11 @@ let defaultConfigDto: FSharpConfigDto = RecordStubGenerationBody = None AddPrivateAccessModifier = None UnusedOpensAnalyzer = None + UnusedOpensAnalyzerExclusions = None UnusedDeclarationsAnalyzer = None + UnusedDeclarationsAnalyzerExclusions = None SimplifyNameAnalyzer = None + SimplifyNameAnalyzerExclusions = None ResolveNamespaces = None EnableReferenceCodeLens = None EnableAnalyzers = None