Skip to content

Commit

Permalink
fixes test discovery and analyzers for adaptive
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAngryByrd committed Feb 8, 2023
1 parent e0b635d commit 4e9465a
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 56 deletions.
52 changes: 27 additions & 25 deletions src/FsAutoComplete.Core/Commands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -896,30 +896,6 @@ module Commands =
highlights |> Array.collect expandParents)
|> Array.sortBy startToken

type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: bool, rootPath: string option) =
let fileParsed = Event<FSharpParseFileResults>()

let fileChecked = Event<ParseAndCheckResults * string<LocalPath> * int>()

let scriptFileProjectOptions = Event<FSharpProjectOptions>()

let disposables = ResizeArray()

let mutable workspaceRoot: string option = rootPath
let mutable linterConfigFileRelativePath: string option = None
// let mutable linterConfiguration: FSharpLint.Application.Lint.ConfigurationParam = FSharpLint.Application.Lint.ConfigurationParam.Default
let mutable lastCheckResult: ParseAndCheckResults option = None

let notify = Event<NotificationEvent>()

let fileStateSet = Event<unit>()
let commandsLogger = LogProvider.getLoggerByName "Commands"

let checkerLogger = LogProvider.getLoggerByName "CheckerEvents"

let fantomasLogger = LogProvider.getLoggerByName "Fantomas"




let analyzerHandler (file: string<LocalPath>, content, pt, tast, symbols, getAllEnts) =
Expand Down Expand Up @@ -967,6 +943,32 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers:

[||]


type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: bool, rootPath: string option) =
let fileParsed = Event<FSharpParseFileResults>()

let fileChecked = Event<ParseAndCheckResults * string<LocalPath> * int>()

let scriptFileProjectOptions = Event<FSharpProjectOptions>()

let disposables = ResizeArray()

let mutable workspaceRoot: string option = rootPath
let mutable linterConfigFileRelativePath: string option = None
// let mutable linterConfiguration: FSharpLint.Application.Lint.ConfigurationParam = FSharpLint.Application.Lint.ConfigurationParam.Default
let mutable lastCheckResult: ParseAndCheckResults option = None

let notify = Event<NotificationEvent>()

let fileStateSet = Event<unit>()
let commandsLogger = LogProvider.getLoggerByName "Commands"

let checkerLogger = LogProvider.getLoggerByName "CheckerEvents"

let fantomasLogger = LogProvider.getLoggerByName "Fantomas"



do
disposables.Add
<| state.ProjectController.Notifications.Subscribe(NotificationEvent.Workspace >> notify.Trigger)
Expand Down Expand Up @@ -1034,7 +1036,7 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers:
| true, fileData ->

let res =
analyzerHandler (
Commands.analyzerHandler (
file,
fileData.Lines.ToString().Split("\n"),
parseAndCheck.GetParseResults.ParseTree,
Expand Down
166 changes: 151 additions & 15 deletions src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -183,23 +183,63 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar

let config = cval<FSharpConfig> FSharpConfig.Default

let checker =
config
|> AVal.map (fun c -> c.EnableAnalyzers) // Maps will cache values and we don't want to recreate FSharpCompilerServiceChecker unless only EnableAnalyzers changed
|> AVal.map (FSharpCompilerServiceChecker)
let analyzersEnabled = config |> AVal.map (fun c -> c.EnableAnalyzers)

let checker = analyzersEnabled |> AVal.map (FSharpCompilerServiceChecker)

let rootPath = cval<string option> None

let mutableConfigChanges =
let toCompilerToolArgument (path: string) = sprintf "--compilertool:%s" path

aval {
let! config = config
and! checker = checker
and! rootPath = rootPath

checker.SetFSIAdditionalArguments
[| yield! config.FSICompilerToolLocations |> Array.map toCompilerToolArgument
yield! config.FSIExtraParameters |]

return ()
if config.EnableAnalyzers then
Loggers.analyzers.info (
Log.setMessage "Using analyzer roots of {roots}"
>> Log.addContextDestructured "roots" config.AnalyzersPath
)

config.AnalyzersPath
|> Array.iter (fun analyzerPath ->
match rootPath with
| None -> ()
| Some workspacePath ->
let dir =
if
System.IO.Path.IsPathRooted analyzerPath
// if analyzer is using absolute path, use it as is
then
analyzerPath
// otherwise, it is a relative path and should be combined with the workspace path
else
System.IO.Path.Combine(workspacePath, analyzerPath)

Loggers.analyzers.info (
Log.setMessage "Loading analyzers from {dir}"
>> Log.addContextDestructured "dir" dir
)

let (n, m) = dir |> FSharp.Analyzers.SDK.Client.loadAnalyzers

Loggers.analyzers.info (
Log.setMessage "From {name}: {dllNo} dlls including {analyzersNo} analyzers"
>> Log.addContextDestructured "name" analyzerPath
>> Log.addContextDestructured "dllNo" n
>> Log.addContextDestructured "analyzersNo" m
))

return ()
else
Loggers.analyzers.info (Log.setMessage "Analyzers disabled")
return ()
}

let updateConfig c =
Expand Down Expand Up @@ -233,6 +273,90 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar

let scriptFileProjectOptions = Event<FSharpProjectOptions>()

let fileParsed =
Event<FSharpParseFileResults * FSharpProjectOptions * CancellationToken>()

let fileChecked = Event<ParseAndCheckResults * VolatileFile * CancellationToken>()


do
disposables.Add
<| fileParsed.Publish.Subscribe(fun (parseResults, proj, ct) ->
logger.info (
Log.setMessage "Test Detection of {file} started"
>> Log.addContextDestructured "file" parseResults.FileName
)

let fn = UMX.tag parseResults.FileName

let res =
if proj.OtherOptions |> Seq.exists (fun o -> o.Contains "Expecto.dll") then
TestAdapter.getExpectoTests parseResults.ParseTree
elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "nunit.framework.dll") then
TestAdapter.getNUnitTest parseResults.ParseTree
elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "xunit.assert.dll") then
TestAdapter.getXUnitTest parseResults.ParseTree
else
[]

logger.info (
Log.setMessage "Test Detection of {file} - {res}"
>> Log.addContextDestructured "file" parseResults.FileName
>> Log.addContextDestructured "res" res
)

notifications.Trigger(NotificationEvent.TestDetected(fn, res |> List.toArray), ct))

do
disposables.Add
<| fileChecked.Publish.Subscribe(fun (parseAndCheck, volatileFile, ct) ->
async {
if analyzersEnabled |> AVal.force then
let file = volatileFile.FileName

try
Loggers.analyzers.info (
Log.setMessage "begin analysis of {file}"
>> Log.addContextDestructured "file" file
)

match parseAndCheck.GetCheckResults.ImplementationFile with
| Some tast ->

let res =
Commands.analyzerHandler (
file,
volatileFile.Lines.ToString().Split("\n"),
parseAndCheck.GetParseResults.ParseTree,
tast,
parseAndCheck.GetCheckResults.PartialAssemblySignature.Entities |> Seq.toList,
parseAndCheck.GetAllEntities
)

notifications.Trigger(NotificationEvent.AnalyzerMessage(res, file), ct)

Loggers.analyzers.info (
Log.setMessage "end analysis of {file}"
>> Log.addContextDestructured "file" file
)

| _ ->
Loggers.analyzers.info (
Log.setMessage "missing components of {file} to run analyzers, skipped them"
>> Log.addContextDestructured "file" file
)

()
with ex ->
Loggers.analyzers.error (
Log.setMessage "Run failed for {file}"
>> Log.addContextDestructured "file" file
>> Log.addExn ex
)
}
|> Async.StartWithCT ct)


let handleCommandEvents (n: NotificationEvent, ct: CancellationToken) =
try
async {
Expand Down Expand Up @@ -457,7 +581,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar

let loader = cval<Ionide.ProjInfo.IWorkspaceLoader> workspaceLoader

let rootPath = cval<string option> None


let binlogConfig =
Expand Down Expand Up @@ -583,7 +706,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
// Set some default values as FCS uses these for identification/caching purposes
let fso =
{ fso with
SourceFiles = fso.SourceFiles |> Array.map(Utils.normalizePath >> UMX.untag)
SourceFiles = fso.SourceFiles |> Array.map (Utils.normalizePath >> UMX.untag)
Stamp = fso.Stamp |> Option.orElse (Some DateTime.UtcNow.Ticks)
ProjectId = fso.ProjectId |> Option.orElse (Some(Guid.NewGuid().ToString())) }

Expand Down Expand Up @@ -875,7 +998,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
match! sourceFileToProjectOptions |> AMap.tryFind filePath with
| None ->
// openFilesToChangesAndProjectOptions contains script files that we may need to look through
match! openFilesToChangesAndProjectOptions |> AMap.tryFindA filePath with
match! openFilesToChangesAndProjectOptions |> AMap.tryFindA filePath with
| None -> return []
| Some (_, projs) -> return projs
| Some projs -> return projs
Expand Down Expand Up @@ -1013,6 +1136,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar

Async.Start(
async {

fileParsed.Trigger(parseAndCheck.GetParseResults, opts, ct)
fileChecked.Trigger(parseAndCheck, file, ct)
let checkErrors = parseAndCheck.GetParseResults.Diagnostics
let parseErrors = parseAndCheck.GetCheckResults.Diagnostics

Expand Down Expand Up @@ -1068,13 +1194,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
let! opts = List.tryHead projectOptions
and! cts = tryGetOpenFileToken file

return!
Debug.measure "parseFile"
<| fun () ->
let opts = Utils.projectOptionsToParseOptions opts
let parseOpts = Utils.projectOptionsToParseOptions opts

checker.ParseFile(file, info.Lines, opts)
|> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token)
let! result =
checker.ParseFile(file, info.Lines, parseOpts)
|> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token)

fileParsed.Trigger(result, opts, cts.Token)
return result
}

})
Expand Down Expand Up @@ -1919,6 +2046,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
do! bypassAdaptiveAndCheckDepenenciesForFile filePath
do! lspClient.CodeLensRefresh()

logger.info (
Log.setMessage "TextDocumentDidSave Request Finished: {parms}"
>> Log.addContextDestructured "parms" p
)

return ()
with e ->
Expand Down Expand Up @@ -2221,7 +2352,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
and! tyRes = forceGetTypeCheckResultsStale filePath |> Result.ofStringErr

match tyRes.TryGetToolTipEnhanced pos lineStr with
| Ok (Some (tip, signature, footer, typeDoc)) ->
| Ok (Some (tip, signature, footer, typeDoc) as x) ->
logger.info (
Log.setMessage "TryGetToolTipEnhanced : {parms}"
>> Log.addContextDestructured "parms" x
)

let formatCommentStyle =
let config = AVal.force config

Expand Down
3 changes: 2 additions & 1 deletion test/FsAutoComplete.Tests.Lsp/CompletionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ let tests state =
}
|> Async.Cache

testList
testSequenced
<| testList
"Completion Tests"
[ testCaseAsync
"simple module member completion on dot"
Expand Down
13 changes: 8 additions & 5 deletions test/FsAutoComplete.Tests.Lsp/CoreTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,11 @@ let tooltipTests state =

let concatLines = String.concat Environment.NewLine

let verifyDescription line character expectedDescription =
let verifyDescriptionImpl testCaseAsync line character expectedDescription =
let expectedDescription = concatLines expectedDescription

testCaseAsync
(sprintf "description for line %d character %d should be '%s" line character expectedDescription)
(sprintf "description for line %d character %d" line character)
(async {
let! server, scriptPath = server

Expand All @@ -241,9 +241,12 @@ let tooltipTests state =
| Ok response -> failtestf "Should have gotten description but got %A" response
| Result.Error errors -> failtestf "Error while getting description: %A" errors
})
let verifyDescription line character expectedDescription = verifyDescriptionImpl testCaseAsync line character expectedDescription
let pverifyDescription reason line character expectedDescription = verifyDescriptionImpl ptestCaseAsync line character expectedDescription
let fverifyDescription line character expectedDescription = verifyDescriptionImpl ftestCaseAsync line character expectedDescription


testList
testSequenced
<| testList
"tooltip evaluation"
[ testList
"tests"
Expand All @@ -260,7 +263,7 @@ let tooltipTests state =
verifySignature 1 5 "val listOfTuples : list<int * int>" // verify we default to prefix-generics style
verifySignature 2 5 "val listOfStructTuples : list<struct (int * int)>" // verify we render struct tuples in a round-tripabble format
verifySignature 3 5 "val floatThatShouldHaveGenericReportedInTooltip : float" // verify we strip <MeasureOne> measure annotations
verifyDescription
pverifyDescription "This test depends on FSharp.Core.xml being in the bin directory of these tests"
4
4
[ "**Description**"
Expand Down
Loading

0 comments on commit 4e9465a

Please sign in to comment.