From 9196777cee49cdec68ae5ba5d710cd61522884c6 Mon Sep 17 00:00:00 2001 From: 1eyewonder Date: Mon, 13 Jan 2025 19:24:28 -0600 Subject: [PATCH 1/5] Updated getting started docs to include FAKE setup --- docs/content/Getting Started Using.md | 63 ++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/content/Getting Started Using.md b/docs/content/Getting Started Using.md index d74c6e2..a1ed702 100644 --- a/docs/content/Getting Started Using.md +++ b/docs/content/Getting Started Using.md @@ -41,6 +41,8 @@ dotnet fsharp-analyzers --project ./YourProject.fsproj --analyzers-path C:\Users As you can see, the path to the analyzer DLL files could be tricky to get right across a wide range of setups. Luckily, we can use an MSBuild custom target to take care of the path construction. +Note: If using MSBuild is not the right solution for you, you can also [call the analyzers from a FAKE build script](#Call-Analyzers-in-Your-FAKE-Build). + Add [FSharp.Analyzers.Build](https://www.nuget.org/packages/FSharp.Analyzers.Build) to your `fsproj`: ```xml @@ -186,4 +188,63 @@ We often add a dummy target to a project to print out some values: Run `dotnet msbuild YourProject.fsproj /t:Dump` and verify that `CodeRoot` has a value or not. -[Next]({{fsdocs-next-page-link}}) +## Call Analyzers in Your FAKE Build + +The below example assumes: + +1. You have the `fsharp-analyzers` dotnet tool installed +2. You are using [FAKE](https://fake.build/) as your build automation +3. You are using [Paket](https://github.com/fsprojects/Paket) as your package manager + +You can adapt this example to work with other build automation tools and package managers. + +```fsharp +open Fake.Api +open Fake.Core +open Fake.DotNet +open Fake.IO +open Fake.IO.Globbing.Operators +open System.IO + +let restore _ = + // this is a dummy example of how you can restore your solution + let setParams : DotNet.RestoreOptions -> DotNet.RestoreOptions = id + DotNet.restore setParams "MySolution.sln" + +let runAnalyzers args = DotNet.exec id "fsharp-analyzers" args + +let analyze _ = + // this example is using paket as our package manager & we have our analyzers in a group called "analyzers" + // however you can grab your analyzers from anywhere + let analyzerPaths = !! "packages/analyzers/**/analyzers/dotnet/fs" + + let createArgsForProject (project: string) analyzerPaths = + [ + "--project" + project + "--analyzers-path" + yield! analyzerPaths + ] + |> String.concat " " + + // use globbing to get all the fsproj files you want to analyze + !! "src/**/*.fsproj" + |> Seq.iter (fun fsproj -> + let result = + createArgsForProject fsproj analyzerPaths + |> runAnalyzers + + result.Errors + |> Seq.iter Trace.traceError + ) + +// other FAKE code here... + +Target.create "Restore" restore +Target.create "Analyzers" analyze + +// example of setting up analyzers in your dependency graph +"Restore" ==> "Analyzers" |> ignore +``` + +[Next]({{fsdocs-next-page-link}}) \ No newline at end of file From 020b96463a94672b066ccdda98733987eca8b4c4 Mon Sep 17 00:00:00 2001 From: 1eyewonder Date: Mon, 13 Jan 2025 22:32:30 -0600 Subject: [PATCH 2/5] Updates per reviewer comments --- docs/content/Getting Started Using.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/Getting Started Using.md b/docs/content/Getting Started Using.md index a1ed702..46c70e6 100644 --- a/docs/content/Getting Started Using.md +++ b/docs/content/Getting Started Using.md @@ -41,7 +41,7 @@ dotnet fsharp-analyzers --project ./YourProject.fsproj --analyzers-path C:\Users As you can see, the path to the analyzer DLL files could be tricky to get right across a wide range of setups. Luckily, we can use an MSBuild custom target to take care of the path construction. -Note: If using MSBuild is not the right solution for you, you can also [call the analyzers from a FAKE build script](#Call-Analyzers-in-Your-FAKE-Build). +Note: If you're using FAKE you can [call the analyzers from a FAKE build script](#Call-Analyzers-in-Your-FAKE-Build). Add [FSharp.Analyzers.Build](https://www.nuget.org/packages/FSharp.Analyzers.Build) to your `fsproj`: From ba24710681b6353cb6364922eb9649d187e7e7db Mon Sep 17 00:00:00 2001 From: 1eyewonder Date: Mon, 20 Jan 2025 21:56:59 -0600 Subject: [PATCH 3/5] Updated docs. Broke apart getting-started into multiple pages --- docs/content/Dual Analyzer.fsx | 4 +- docs/content/Getting Started Writing.fsx | 4 +- docs/content/Programmatic access.fsx | 4 +- docs/content/Running during CI.md | 4 +- docs/content/Unit Testing.fsx | 4 +- docs/content/getting-started/CLI.md | 33 ++++ .../getting-started/Configuring for IDE.md | 26 +++ .../getting-started/Installing Analyzers.md | 50 ++++++ .../MSBuild.md} | 170 ++++++------------ docs/images/analyzers-inline-warning.png | Bin 0 -> 19378 bytes docs/index.md | 2 +- 11 files changed, 179 insertions(+), 122 deletions(-) create mode 100644 docs/content/getting-started/CLI.md create mode 100644 docs/content/getting-started/Configuring for IDE.md create mode 100644 docs/content/getting-started/Installing Analyzers.md rename docs/content/{Getting Started Using.md => getting-started/MSBuild.md} (54%) create mode 100644 docs/images/analyzers-inline-warning.png diff --git a/docs/content/Dual Analyzer.fsx b/docs/content/Dual Analyzer.fsx index 1278854..c6bfbf7 100644 --- a/docs/content/Dual Analyzer.fsx +++ b/docs/content/Dual Analyzer.fsx @@ -1,8 +1,8 @@ (** --- category: end-users -categoryindex: 1 -index: 3 +categoryindex: 2 +index: 2 --- # Writing an analyzer for both console and editor diff --git a/docs/content/Getting Started Writing.fsx b/docs/content/Getting Started Writing.fsx index bba5e0b..9fc9575 100644 --- a/docs/content/Getting Started Writing.fsx +++ b/docs/content/Getting Started Writing.fsx @@ -1,8 +1,8 @@ (** --- category: end-users -categoryindex: 1 -index: 2 +categoryindex: 2 +index: 1 --- # Getting started writing an analyzer diff --git a/docs/content/Programmatic access.fsx b/docs/content/Programmatic access.fsx index 6f75f36..c034e5f 100644 --- a/docs/content/Programmatic access.fsx +++ b/docs/content/Programmatic access.fsx @@ -1,8 +1,8 @@ (** --- category: end-users -categoryindex: 1 -index: 4 +categoryindex: 2 +index: 3 --- # Programmatically running an analyzer diff --git a/docs/content/Running during CI.md b/docs/content/Running during CI.md index 1587a8e..8ce60b8 100644 --- a/docs/content/Running during CI.md +++ b/docs/content/Running during CI.md @@ -1,7 +1,7 @@ --- category: end-users -categoryindex: 1 -index: 6 +categoryindex: 2 +index: 5 --- # Running analyzers during continuous integration diff --git a/docs/content/Unit Testing.fsx b/docs/content/Unit Testing.fsx index 4e14c30..c0e0ef0 100644 --- a/docs/content/Unit Testing.fsx +++ b/docs/content/Unit Testing.fsx @@ -1,8 +1,8 @@ (** --- category: end-users -categoryindex: 1 -index: 5 +categoryindex: 2 +index: 4 --- # Unit testing an analyzer diff --git a/docs/content/getting-started/CLI.md b/docs/content/getting-started/CLI.md new file mode 100644 index 0000000..346bf96 --- /dev/null +++ b/docs/content/getting-started/CLI.md @@ -0,0 +1,33 @@ +--- +category: getting-started +categoryindex: 1 +index: 3 +--- + +# Command Line Arguments + +## Example Command + +When running the CLI tool from the command line, the bare minimum you need to provide is the path to the project file(s) you want to analyze. + +```shell +dotnet fsharp-analyzers --project ./YourProject.fsproj +``` + +An optional argument you may need to provide is `--analyzers-path`. This is the path to the directory containing the analyzer DLLs. + +```shell +dotnet fsharp-analyzers --project ./YourProject.fsproj --analyzers-path ./path/to/analyzers/directory +``` + +⚠️ If you don't provide this argument, it will default to `packages/analyzers`. + +## Viewing Additional Commands + +You can view the full list of commands available by running: + +```shell +dotnet fsharp-analyzers --help +``` + +[Next]({{fsdocs-next-page-link}}) \ No newline at end of file diff --git a/docs/content/getting-started/Configuring for IDE.md b/docs/content/getting-started/Configuring for IDE.md new file mode 100644 index 0000000..460854f --- /dev/null +++ b/docs/content/getting-started/Configuring for IDE.md @@ -0,0 +1,26 @@ +--- +category: getting-started +categoryindex: 1 +index: 2 +--- + +# Configuring for the IDE + +## Visual Studio Code + +In order to configure analyzers for VSCode, you will need to update your project's `.vscode/settings.json` file or your user settings. You should only need the following settings: + +```json +{ + "FSharp.enableAnalyzers": true, + "FSharp.analyzersPath": ["packages/analyzers"] +} +``` + +📓 Note: The path in `FSharp.analyzersPath` above is currently pointing to the path we set up in the Paket example on the [installation page]({{fsdocs-previous-page-link}}). + +After saving your new settings, make sure to restart VSCode. Once VSCode restarts, you should be able to test and see if the analyzers are working by opening a F# file in your workspace and entering the following code + +![Analyzers Inline Warning](../../images/analyzers-inline-warning.png) + +[Next]({{fsdocs-next-page-link}}) \ No newline at end of file diff --git a/docs/content/getting-started/Installing Analyzers.md b/docs/content/getting-started/Installing Analyzers.md new file mode 100644 index 0000000..2f33508 --- /dev/null +++ b/docs/content/getting-started/Installing Analyzers.md @@ -0,0 +1,50 @@ +--- +category: getting-started +categoryindex: 1 +index: 1 +--- +# Installation + +## Installing the Tool + +A dotnet CLI tool, called [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK/), is used to run analyzers outside the context of an IDE. Add it to your tool-manifest with: + +```shell +dotnet tool install fsharp-analyzers +``` + +## Installing Analyzers + +### Suggested Packages + +1. [Ionide Analyzers](https://github.com/ionide/FSharp.Analyzers.SDK/) +2. [G-Research Analyzers](https://github.com/G-Research/fsharp-analyzers/) + +### Nuget + +If you are using Nuget as your package manager, add the `PackageReference` pointing to your favorite analyzers to the `.fsproj` file of the project you want to analyze. + +```xml + + all + analyzers + + + all + analyzers + +``` + +### Paket + +If you are using Paket as your package manager, add the package to your `paket.dependencies` file. The example below uses a paket group, but it is not required. + +```paket +group analyzers + source https://api.nuget.org/v3/index.json + + nuget Ionide.Analyzers + nuget G-Research.FSharp.Analyzers +``` + +[Next]({{fsdocs-next-page-link}}) \ No newline at end of file diff --git a/docs/content/Getting Started Using.md b/docs/content/getting-started/MSBuild.md similarity index 54% rename from docs/content/Getting Started Using.md rename to docs/content/getting-started/MSBuild.md index 46c70e6..694db1a 100644 --- a/docs/content/Getting Started Using.md +++ b/docs/content/getting-started/MSBuild.md @@ -1,59 +1,45 @@ --- -category: end-users +category: getting-started categoryindex: 1 -index: 1 +index: 4 --- -# Getting started using analyzers +# MSBuild -## Premise +## Using Analyzer Build Target in a Project -We assume the analyzers you want to use are distributed as a nuget package. +The path to the analyzer DLL files could be tricky to get right across a wide range of setups. Luckily, we can use a MSBuild custom target to take care of the path construction. Add [FSharp.Analyzers.Build](https://www.nuget.org/packages/FSharp.Analyzers.Build) to your project. This imports a new target to your project file (`AnalyzeFSharpProject`) and will allow us to easily run the analyzers for our project. -## Using analyzers in a single project +### Installing Target via Nuget -### Raw command line - -A dotnet CLI tool, called [fsharp-analyzers](https://www.nuget.org/packages/fsharp-analyzers), is used to run analyzers outside the context of an IDE. -Add it to your tool-manifest with: -```shell -dotnet tool install fsharp-analyzers -``` - -Next, add the `PackageReference` pointing to your favorite analyzers to the `.fsproj` file of the project you want to analyze: +If you are using Nuget, add it to your `.fsproj` file: ```xml - + all - analyzers + build ``` -At the time of writing, the [G-Research analyzers](https://github.com/g-research/fsharp-analyzers) [package](https://www.nuget.org/packages/G-Research.FSharp.Analyzers) contains the only analyzers compatible with the latest CLI tool. -With the package downloaded, we can run the CLI tool: +### Installing Target via Paket -```shell -dotnet fsharp-analyzers --project ./YourProject.fsproj --analyzers-path C:\Users\yourusername\.nuget\packages\g-research.fsharp.analyzers\0.4.0\analyzers\dotnet\fs\ --verbosity d -``` - -### Using an MSBuild target +If you are using Paket, add it to your `paket.dependencies` -As you can see, the path to the analyzer DLL files could be tricky to get right across a wide range of setups. -Luckily, we can use an MSBuild custom target to take care of the path construction. +```paket +group analyzers + source https://api.nuget.org/v3/index.json -Note: If you're using FAKE you can [call the analyzers from a FAKE build script](#Call-Analyzers-in-Your-FAKE-Build). + nuget FSharp.Analyzers.Build +``` -Add [FSharp.Analyzers.Build](https://www.nuget.org/packages/FSharp.Analyzers.Build) to your `fsproj`: +as well as the `paket.references` of your project: -```xml - - all - build - +```paket +group analyzers + FSharp.Analyzers.Build ``` -This imports a new target to your project file: `AnalyzeFSharpProject`. -And will allow us to easily run the analyzers for our project. +### Configuring the Build Target Before we can run `dotnet msbuild /t:AnalyzeFSharpProject`, we need to specify our settings in a property called `FSharpAnalyzersOtherFlags`: @@ -64,21 +50,25 @@ Before we can run `dotnet msbuild /t:AnalyzeFSharpProject`, we need to specify o ``` To locate the analyzer DLLs in the filesystem, we use the variable `$(PkgG-Research_FSharp_Analyzers)`. It's produced by NuGet and normalized to be usable by [MSBuild](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty). -In general, a `Pkg` prefix is added and dots in the package ID are replaced by underscores. But make sure to look at the [nuget.g.props](https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#restore-outputs) file in the `obj` folder for the exact string. +In general, a `Pkg` prefix is added and dots in the package ID are replaced by underscores. But make sure to look at the [nuget.g.props](https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#restore-outputs) file in the `obj` folder for the exact string. + The `/analyzers/dotnet/fs` subpath is a convention analyzer authors should follow when creating their packages. +### Running the Build Target + At last, you can run the analyzer from the project folder: ```shell dotnet msbuild /t:AnalyzeFSharpProject ``` -Note: if your project has multiple `TargetFrameworks` the tool will be invoked for each target framework. +📓 Note: If your project has multiple `TargetFrameworks` the tool will be invoked for each target framework. + +## Using Analyzer Build Target in a Solution -## Using analyzers in a solution +Adding the custom target from above to all `.fsproj` files of a solution doesn't scale very well. We can use the MSBuild infrastructure to add the needed package reference and the MSBuild target to all projects in one go. -Adding the custom target from above to all `.fsproj` files of a solution doesn't scale very well. -So we use the MSBuild infrastructure to add the needed package reference and the MSBuild target to all projects in one go. +### Setting up Directory.Build.props We start with adding the `PackageReference` pointing to your favorite analyzers to the [Directory.Build.props](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=vs-2022) file. This adds the package reference to all `.fsproj` files that are in a subfolder of the file location of `Directory.Build.props`: @@ -96,8 +86,10 @@ This adds the package reference to all `.fsproj` files that are in a subfolder o ``` -Likewise we add the `FSharpAnalyzersOtherFlags` property to the [Directory.Build.targets](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=vs-2022) file. -This is effectively the same as adding a property to each `*proj` file which exists in a subfolder. +### Setting up Directory.Build.targets + +Likewise we add the `FSharpAnalyzersOtherFlags` property to the [Directory.Build.targets](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=vs-2022) file. For first time MSBuild users, this is effectively the same as adding a property to each `*proj` file which exists in a subfolder. + ```xml @@ -108,10 +100,9 @@ This is effectively the same as adding a property to each `*proj` file which exi ``` -⚠️ We are adding the `FSharpAnalyzersOtherFlags` property to our **Directory.Build.targets** and **not to** any **Directory.Build.props** file! -MSBuild will first evaluate `Directory.Build.props` which has no access to the generated nuget.g.props. `$(PkgG-Research_FSharp_Analyzers)` won't be known at this point. `Directory.Build.targets` is evaluated after the project file and has access to `Pkg` generated properties. +⚠️ We are adding the `FSharpAnalyzersOtherFlags` property to our **Directory.Build.targets** and **not to** any **Directory.Build.props** file! MSBuild will first evaluate `Directory.Build.props` which has no access to the generated nuget.g.props. `$(PkgG-Research_FSharp_Analyzers)` won't be known at this point. `Directory.Build.targets` is evaluated after the project file and has access to `Pkg` generated properties. -### All projects in the solution +### Run Target for All Projects in the Solution We can run the `AnalyzeFSharpProject` target against all projects in a solution @@ -119,9 +110,9 @@ We can run the `AnalyzeFSharpProject` target against all projects in a solution dotnet msbuild YourSolution.sln /t:AnalyzeFSharpProject ``` -### Select specific projects +### Configuring Specific Projects to Run -As we don't want to target all projects in the solution, we create a second custom MSBuild target that calls the project-specific target for all relevant projects. +As we may not always want to target every project in a solution, we can create a second custom MSBuild target that calls the project-specific target for all relevant projects. Add the following custom target to the [Directory.Solution.targets](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-solution-build?view=vs-2022) file to be able to invoke analysis from all selected projects in one simple command: ```xml @@ -129,13 +120,29 @@ Add the following custom target to the [Directory.Solution.targets](https://lear + + + + + +``` + +You can also exclude certain projects from the analysis if they fall within the same pattern +```xml + + + + + ``` +### Running the Solution Target + At last, you can run the analyzer from the solution folder: ```shell @@ -144,15 +151,15 @@ dotnet msbuild /t:AnalyzeSolution Note: we passed the `--code-root` flag so that the `*.sarif` report files will report file paths relative to this root. This can be imported for certain editors to function properly. -## MSBuild tips and tricks +## MSBuild Tips and Tricks MSBuild can be overwhelming for the uninitiated. Here are some tricks we've seen in the wild: -### Use well-known properties +### Use Well-Known Properties Checkout the [MSBuild reserved and well-known properties](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2022) to use existing variables like `$(MSBuildProjectFile)`. -### Wrap path arguments in quotes +### Wrap Path Arguments in Quotes As MSBuild is all XML, you can use `"` to wrap evaluated values in quotes: @@ -162,7 +169,7 @@ As MSBuild is all XML, you can use `"` to wrap evaluated values in quotes: ``` -### Extend `` in multiple lines +### Extend `` in Multiple Lines You can extend the value of `$(FSharpAnalyzersOtherFlags)` by setting it again in multiple lines: @@ -175,7 +182,7 @@ You can extend the value of `$(FSharpAnalyzersOtherFlags)` by setting it again i ``` -### Verify parameters are present +### Verify Parameters are Present It can be a bit confusing to find out if a variable contains the value you think it does. We often add a dummy target to a project to print out some values: @@ -188,63 +195,4 @@ We often add a dummy target to a project to print out some values: Run `dotnet msbuild YourProject.fsproj /t:Dump` and verify that `CodeRoot` has a value or not. -## Call Analyzers in Your FAKE Build - -The below example assumes: - -1. You have the `fsharp-analyzers` dotnet tool installed -2. You are using [FAKE](https://fake.build/) as your build automation -3. You are using [Paket](https://github.com/fsprojects/Paket) as your package manager - -You can adapt this example to work with other build automation tools and package managers. - -```fsharp -open Fake.Api -open Fake.Core -open Fake.DotNet -open Fake.IO -open Fake.IO.Globbing.Operators -open System.IO - -let restore _ = - // this is a dummy example of how you can restore your solution - let setParams : DotNet.RestoreOptions -> DotNet.RestoreOptions = id - DotNet.restore setParams "MySolution.sln" - -let runAnalyzers args = DotNet.exec id "fsharp-analyzers" args - -let analyze _ = - // this example is using paket as our package manager & we have our analyzers in a group called "analyzers" - // however you can grab your analyzers from anywhere - let analyzerPaths = !! "packages/analyzers/**/analyzers/dotnet/fs" - - let createArgsForProject (project: string) analyzerPaths = - [ - "--project" - project - "--analyzers-path" - yield! analyzerPaths - ] - |> String.concat " " - - // use globbing to get all the fsproj files you want to analyze - !! "src/**/*.fsproj" - |> Seq.iter (fun fsproj -> - let result = - createArgsForProject fsproj analyzerPaths - |> runAnalyzers - - result.Errors - |> Seq.iter Trace.traceError - ) - -// other FAKE code here... - -Target.create "Restore" restore -Target.create "Analyzers" analyze - -// example of setting up analyzers in your dependency graph -"Restore" ==> "Analyzers" |> ignore -``` - [Next]({{fsdocs-next-page-link}}) \ No newline at end of file diff --git a/docs/images/analyzers-inline-warning.png b/docs/images/analyzers-inline-warning.png new file mode 100644 index 0000000000000000000000000000000000000000..16f741792eb4f8d3f74b59ce7dc62465ef7a3e78 GIT binary patch literal 19378 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 ZXZ?;24^lS-eB{|;Gcml3s?>Lh`Zvy8#p3_~ literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index ed1c374..35743d5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,7 +22,7 @@ You can also set up a run configuration of FSharp.Analyzers.Cli in your favorite ## Using Analyzers -Checkout our [Getting Started](https://ionide.io/FSharp.Analyzers.SDK/content/Getting%20Started%20Using.html) guide for analyzer users! +Checkout our [Getting Started](https://ionide.io/FSharp.Analyzers.SDK/content/getting-started/Installing%20Analyzers.html) guide for analyzer users! ## Writing Analyzers From 67bf90a64f01be8c09d041f4edd0036986f8604f Mon Sep 17 00:00:00 2001 From: 1eyewonder Date: Mon, 27 Jan 2025 22:38:49 -0600 Subject: [PATCH 4/5] Updated docs per reviwer comments --- docs/content/getting-started/CLI.md | 10 ++-------- docs/content/getting-started/Configuring for IDE.md | 6 +++--- docs/content/getting-started/Installing Analyzers.md | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/content/getting-started/CLI.md b/docs/content/getting-started/CLI.md index 346bf96..b786c2d 100644 --- a/docs/content/getting-started/CLI.md +++ b/docs/content/getting-started/CLI.md @@ -8,19 +8,13 @@ index: 3 ## Example Command -When running the CLI tool from the command line, the bare minimum you need to provide is the path to the project file(s) you want to analyze. - -```shell -dotnet fsharp-analyzers --project ./YourProject.fsproj -``` - -An optional argument you may need to provide is `--analyzers-path`. This is the path to the directory containing the analyzer DLLs. +When running the CLI tool from the command line (and after installing analyzers), the minimum console arguments you need to provide is the path to the project file(s) you want to analyze. ```shell dotnet fsharp-analyzers --project ./YourProject.fsproj --analyzers-path ./path/to/analyzers/directory ``` -⚠️ If you don't provide this argument, it will default to `packages/analyzers`. +⚠️ If you don't provide the `--analyzers-path` argument, it will default to `packages/analyzers`. If you are using Paket with a group called `analyzers`, this default path should work for you. ## Viewing Additional Commands diff --git a/docs/content/getting-started/Configuring for IDE.md b/docs/content/getting-started/Configuring for IDE.md index 460854f..04f826f 100644 --- a/docs/content/getting-started/Configuring for IDE.md +++ b/docs/content/getting-started/Configuring for IDE.md @@ -8,16 +8,16 @@ index: 2 ## Visual Studio Code -In order to configure analyzers for VSCode, you will need to update your project's `.vscode/settings.json` file or your user settings. You should only need the following settings: +In order to configure analyzers for VSCode, you will need to update your project's `.vscode/settings.json` file or your user settings. You should need the settings shown below. ```json { "FSharp.enableAnalyzers": true, - "FSharp.analyzersPath": ["packages/analyzers"] + "FSharp.analyzersPath": ["path/to/analyzers/directory"] } ``` -📓 Note: The path in `FSharp.analyzersPath` above is currently pointing to the path we set up in the Paket example on the [installation page]({{fsdocs-previous-page-link}}). +📓 Note: Issue created [here](https://github.com/ionide/FsAutoComplete/issues/1350) regarding analyzers & SDK mismatches in the logs After saving your new settings, make sure to restart VSCode. Once VSCode restarts, you should be able to test and see if the analyzers are working by opening a F# file in your workspace and entering the following code diff --git a/docs/content/getting-started/Installing Analyzers.md b/docs/content/getting-started/Installing Analyzers.md index 2f33508..c91ccb9 100644 --- a/docs/content/getting-started/Installing Analyzers.md +++ b/docs/content/getting-started/Installing Analyzers.md @@ -10,7 +10,7 @@ index: 1 A dotnet CLI tool, called [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK/), is used to run analyzers outside the context of an IDE. Add it to your tool-manifest with: ```shell -dotnet tool install fsharp-analyzers +dotnet tool install fsharp-analyzers --create-manifest-if-needed ``` ## Installing Analyzers From 6798d4d07b9d0c228a9e758a7c24c8ca02ba6f9f Mon Sep 17 00:00:00 2001 From: 1eyewonder Date: Tue, 28 Jan 2025 01:11:40 -0600 Subject: [PATCH 5/5] Updated unit test project options --- samples/OptionAnalyzer.Test/UnitTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/OptionAnalyzer.Test/UnitTests.fs b/samples/OptionAnalyzer.Test/UnitTests.fs index 5af620a..c1f0266 100644 --- a/samples/OptionAnalyzer.Test/UnitTests.fs +++ b/samples/OptionAnalyzer.Test/UnitTests.fs @@ -13,7 +13,7 @@ let Setup () = task { let! opts = mkOptionsFromProject - "net7.0" + "net8.0" [ { Name = "Newtonsoft.Json"