Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only load the .fantomasignore file once, except for the daemon #2097

Merged
merged 12 commits into from
Mar 27, 2022
3 changes: 3 additions & 0 deletions docs/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,9 @@ Exclusion applies both to formatting and the format checking.
*.fsx
```

Note that Fantomas only searches for a `.fantomasignore` file in or above its current working directory, if one exists; unlike Git, it does not traverse the filesystem for each input file to find an appropriate ignore file.
(This is not true of the Fantomas daemon. The daemon can't rely on being invoked from the right place, and indeed there may not even be a well-defined notion of "right place" for the formatting tasks the daemon is required to perform, so it does search the filesystem for every file individually.)

## Using the API

See [CodeFormatter.fsi](../src/Fantomas/CodeFormatter.fsi) to view the public API of Fantomas.
Expand Down
9 changes: 9 additions & 0 deletions paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ NUGET
System.Runtime (>= 4.3)
System.Text.Encoding (>= 4.3)
System.Threading.Tasks (>= 4.3)
System.IO.Abstractions (16.1.10)
System.IO.FileSystem.AccessControl (>= 4.7) - restriction: || (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard2.1)) (== netcoreapp3.1) (== netstandard2.0)
System.IO.Abstractions.TestingHelpers (16.1.10)
System.IO.Abstractions (>= 16.1.10)
System.IO.FileSystem (4.3)
Microsoft.NETCore.Platforms (>= 1.1)
Microsoft.NETCore.Targets (>= 1.1)
Expand All @@ -336,6 +340,11 @@ NUGET
System.Runtime.Handles (>= 4.3)
System.Text.Encoding (>= 4.3)
System.Threading.Tasks (>= 4.3)
System.IO.FileSystem.AccessControl (5.0) - restriction: || (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard2.1)) (== netcoreapp3.1) (== netstandard2.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)) (&& (== netcoreapp3.1) (>= monoandroid) (< netstandard1.3)) (&& (== netcoreapp3.1) (>= monotouch)) (&& (== netcoreapp3.1) (< netcoreapp2.0)) (&& (== netcoreapp3.1) (>= xamarinios)) (&& (== netcoreapp3.1) (>= xamarinmac)) (&& (== netcoreapp3.1) (>= xamarintvos)) (&& (== netcoreapp3.1) (>= xamarinwatchos)) (== netstandard2.0)
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (&& (== netcoreapp3.1) (< netcoreapp2.0)) (&& (== netcoreapp3.1) (< netcoreapp2.1)) (&& (== netcoreapp3.1) (>= uap10.1)) (== netstandard2.0)
System.Security.AccessControl (>= 5.0)
System.Security.Principal.Windows (>= 5.0)
System.IO.FileSystem.Primitives (4.3)
System.Runtime (>= 4.3)
System.Linq (4.3)
Expand Down
6 changes: 5 additions & 1 deletion src/Fantomas.CoreGlobalTool/Daemon.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
open System
open System.Diagnostics
open System.IO
open System.IO.Abstractions
open System.Threading
open System.Threading.Tasks
open FSharp.Compiler.Text.Range
Expand All @@ -15,6 +16,7 @@ open Fantomas
open Fantomas.SourceOrigin
open Fantomas.FormatConfig
open Fantomas.Extras.EditorConfig
open Fantomas.Extras

type FantomasDaemon(sender: Stream, reader: Stream) as this =
let rpc: JsonRpc = JsonRpc.Attach(sender, reader, this)
Expand All @@ -30,6 +32,8 @@ type FantomasDaemon(sender: Stream, reader: Stream) as this =

let exit () = disconnectEvent.Set() |> ignore

let fs = FileSystem()

do rpc.Disconnected.Add(fun _ -> exit ())

interface IDisposable with
Expand All @@ -44,7 +48,7 @@ type FantomasDaemon(sender: Stream, reader: Stream) as this =
[<JsonRpcMethod(Methods.FormatDocument, UseSingleObjectParameterDeserialization = true)>]
member _.FormatDocumentAsync(request: FormatDocumentRequest) : Task<FormatDocumentResponse> =
async {
if Fantomas.Extras.IgnoreFile.isIgnoredFile request.FilePath then
if IgnoreFile.isIgnoredFile (IgnoreFile.find fs IgnoreFile.loadIgnoreList request.FilePath) request.FilePath then
return FormatDocumentResponse.IgnoredFile request.FilePath
else
let config =
Expand Down
9 changes: 5 additions & 4 deletions src/Fantomas.CoreGlobalTool/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ let rec allFiles isRec path =
|> Seq.filter (fun f ->
isFSharpFile f
&& not (isInExcludedDir f)
&& not (IgnoreFile.isIgnoredFile f))
&& not (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) f))

/// Fantomas assumes the input files are UTF-8
/// As is stated in F# language spec: https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf#page=25
Expand Down Expand Up @@ -209,7 +209,7 @@ let runCheckCommand (recurse: bool) (inputPath: InputPath) : int =
| InputPath.StdIn _ ->
eprintfn "No input path provided. Nothing to do."
0
| InputPath.File f when (IgnoreFile.isIgnoredFile f) ->
| InputPath.File f when (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) f) ->
printfn "'%s' was ignored" f
0
| InputPath.File path ->
Expand Down Expand Up @@ -418,7 +418,7 @@ let main argv =
let filesAndFolders (files: string list) (folders: string list) : unit =
files
|> List.iter (fun file ->
if (IgnoreFile.isIgnoredFile file) then
if (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file) then
printfn "'%s' was ignored" file
else
processFile file file)
Expand Down Expand Up @@ -448,7 +448,8 @@ let main argv =
| InputPath.Unspecified, _ ->
eprintfn "Input path is missing..."
exit 1
| InputPath.File f, _ when (IgnoreFile.isIgnoredFile f) -> printfn "'%s' was ignored" f
| InputPath.File f, _ when (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) f) ->
printfn "'%s' was ignored" f
| InputPath.Folder p1, OutputPath.NotKnown -> processFolder p1 p1
| InputPath.File p1, OutputPath.NotKnown -> processFile p1 p1
| InputPath.File p1, OutputPath.IO p2 -> processFile p1 p2
Expand Down
7 changes: 7 additions & 0 deletions src/Fantomas.Extras/AssemblyInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Fantomas.Extras.AssemblyInfo

open System.Runtime.CompilerServices

[<assembly: InternalsVisibleTo("Fantomas.Tests")>]

do ()
9 changes: 6 additions & 3 deletions src/Fantomas.Extras/FakeHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ let private formatContentInternalAsync
(file: string)
(originalContent: string)
: Async<FormatResult> =
if IgnoreFile.isIgnoredFile file then
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file then
async { return IgnoredFile file }
else
async {
Expand Down Expand Up @@ -99,7 +99,7 @@ let formatContentAsync = formatContentInternalAsync false
let private formatFileInternalAsync (compareWithoutLineEndings: bool) (file: string) =
let config = EditorConfig.readConfiguration file

if IgnoreFile.isIgnoredFile file then
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file then
async { return IgnoredFile file }
else
let originalContent = File.ReadAllText file
Expand Down Expand Up @@ -167,7 +167,10 @@ let checkCode (filenames: seq<string>) =
async {
let! formatted =
filenames
|> Seq.filter (IgnoreFile.isIgnoredFile >> not)
|> Seq.filter (
IgnoreFile.isIgnoredFile (IgnoreFile.current.Force())
>> not
)
|> Seq.map (formatFileInternalAsync true)
|> Async.Parallel

Expand Down
1 change: 1 addition & 0 deletions src/Fantomas.Extras/Fantomas.Extras.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<None Include="paket.references" />
<Compile Include="AssemblyInfo.fs" />
<Compile Include="EditorConfig.fs" />
<Compile Include="IgnoreFile.fs" />
<Compile Include="FakeHelpers.fs" />
Expand Down
80 changes: 54 additions & 26 deletions src/Fantomas.Extras/IgnoreFile.fs
Original file line number Diff line number Diff line change
@@ -1,47 +1,75 @@
namespace Fantomas.Extras

open System.IO.Abstractions
open MAB.DotIgnore

/// The string argument is taken relative to the location
/// of the ignore-file.
type IsPathIgnored = string -> bool

type IgnoreFile =
{ Location: IFileInfo
IsIgnored: IsPathIgnored }

[<RequireQualifiedAccess>]
module IgnoreFile =

open System.IO
open MAB.DotIgnore

[<Literal>]
let IgnoreFileName = ".fantomasignore"

let rec private findIgnoreFile (filePath: string) : string option =
let allParents =
let rec addParent (di: DirectoryInfo) (finalContinuation: string list -> string list) =
if isNull di.Parent then
finalContinuation [ di.FullName ]
else
addParent di.Parent (fun parents -> di.FullName :: parents |> finalContinuation)
/// Find the `.fantomasignore` file above the given filepath, if one exists.
/// Note that this is intended for use only in the daemon; the command-line tool
/// does not support `.fantomasignore` files anywhere other than the current
/// working directory.
let find (fs: IFileSystem) (loadIgnoreList: string -> IsPathIgnored) (filePath: string) : IgnoreFile option =
let rec walkUp (currentDirectory: IDirectoryInfo) : IgnoreFile option =
if isNull currentDirectory then
None
else
let potentialFile =
fs.Path.Combine(currentDirectory.FullName, IgnoreFileName)
|> fs.FileInfo.FromFileName

addParent (Directory.GetParent filePath) id
if potentialFile.Exists then
{ Location = potentialFile
IsIgnored = loadIgnoreList potentialFile.FullName }
|> Some
else
walkUp currentDirectory.Parent

allParents
|> List.tryFind (fun p -> Path.Combine(p, IgnoreFileName) |> File.Exists)
|> Option.map (fun p -> Path.Combine(p, IgnoreFileName))
walkUp (fs.FileInfo.FromFileName(filePath).Directory)

let private relativePathPrefix = sprintf ".%c" Path.DirectorySeparatorChar
let loadIgnoreList (path: string) : IsPathIgnored =
let list = IgnoreList(path)
fun path -> list.IsIgnored(path, false)

let private removeRelativePathPrefix (path: string) =
if path.StartsWith(relativePathPrefix) then
path.Substring(2)
else
path
let internal current' (fs: IFileSystem) (currentDirectory: string) (loadIgnoreList: string -> IsPathIgnored) =
lazy find fs loadIgnoreList (fs.Path.Combine(currentDirectory, "_"))

let isIgnoredFile (file: string) =
let fullPath = Path.GetFullPath(file)
/// When executed from the command line, Fantomas will not dynamically locate
/// the most appropriate `.fantomasignore` for each input file; it only finds
/// a single `.fantomasignore` file. This is that file.
let current: Lazy<IgnoreFile option> =
current' (FileSystem()) System.Environment.CurrentDirectory loadIgnoreList

match findIgnoreFile fullPath with
let isIgnoredFile (ignoreFile: IgnoreFile option) (file: string) : bool =
match ignoreFile with
| None -> false
| Some ignoreFile ->
let ignores = IgnoreList(ignoreFile)
let fs = ignoreFile.Location.FileSystem
let fullPath = fs.Path.GetFullPath(file)

try
let path = removeRelativePathPrefix file
ignores.IsIgnored(path, false)
let path =
if fullPath.StartsWith ignoreFile.Location.Directory.FullName then
fullPath.[ignoreFile.Location.Directory.FullName.Length + 1 ..]
else
// This scenario is a bit unexpected - it suggests that we are
// trying to ask an ignorefile whether a file that is outside
// its dependency tree is ignored.
fullPath

ignoreFile.IsIgnored path
with
| ex ->
printfn "%A" ex
Expand Down
3 changes: 2 additions & 1 deletion src/Fantomas.Extras/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ FSharp.Core
Ionide.KeepAChangelog.Tasks copy_local: true
Dotnet.ReproducibleBuilds copy_local: true
editorconfig
MAB.DotIgnore
MAB.DotIgnore
System.IO.Abstractions
1 change: 1 addition & 0 deletions src/Fantomas.Tests/Fantomas.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<Compile Include="IndexSyntaxTests.fs" />
<Compile Include="InsertFinalNewlineTests.fs" />
<Compile Include="SingleExprTests.fs" />
<Compile Include="IgnoreFileTests.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Fantomas.Extras\Fantomas.Extras.fsproj" />
Expand Down
Loading