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

Ability To Turn Off Formatting For Subdirectory #873

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

samdeane
Copy link

@samdeane samdeane commented Nov 7, 2024

Draft implementation of #870.

This implementation adds the skipAll flag to Configuration.

The user can set the flag manually, but we also search for a file called .no-swift-format in the same places as we look for .swift-format. If we it, we return a Configuration with skipAll set.

Whilst formatting, if skipAll is set, we output the source file unmodified.

Whilst linting, if skipAll is set, we perform no checks and output no diagnostics.

This implementation adds a check for a .swift-format-ignore file, in the same places that a .swift-format file could be.

The presence of this file skips processing of any files in the containing directory, including those in subdirectories.

Configuration File Precedence

Note that the presence of .swift-format-ignore is checked first at any given directory level, and so it overrides any .swift-format file at the same level.

The logic behind this choice is that allows .swift-format-ignore to be added to .gitignore, and to be created locally, even if a subdirectory already contains a .swift-format file that has been committed.

Other choices could be made here, and there are pros & cons either way.

Motivation

My specific use case (which motivated this change) was a scenario where a gitsubmodule had a .swift-format file at its root, but automated formatting was not yet in use, and the bulk of the module's code was not formatted according to the .swift-format settings.

In this scenario I have a .swift-format file at the root of my larger workspace, and have turned on format-on-save globally for the whole workspace.

If I made a small edit to a file in the submodule, it was being formatted using the submodule's .swift-format file -- which was a problem as it then generated a large diff unrelated to my edit.

I wanted to disable this formatting on my local machine, in the submodule only, but leave the .swift-format file in place for both my workspace and the submodule.

@samdeane samdeane marked this pull request as ready for review November 7, 2024 11:15
@samdeane samdeane marked this pull request as draft November 7, 2024 11:24
@samdeane samdeane marked this pull request as ready for review November 7, 2024 11:39
Copy link
Member

@allevato allevato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this! I think it's a useful feature to have. Some high-level comments:

  • I think the name of the file should be .swift-format-ignore, which aligns nicely with the name used in comments that the formatter supports, and also similar tools like .gitignore and .prettierignore.

  • Lowering this down into the SwiftFormatter and SwiftLinter APIs feels like the wrong approach, because it means the tool is still doing all the work of iterating over and parsing all the files that are going to be ignored anyway.

For the second point, raising this check into the frontend seems more appropriate. Maybe FileIterator should be responsible for looking for the .swift-format-ignore file in the directory or its parents, and if one is found, it doesn't recurse into the directory further? That may end up being simpler to implement (you don't have to touch the configuration at all).

It means that somebody using the SwiftFormatter/SwiftLinter APIs directly wouldn't get that logic, but I think that's fine; those APIs are meant to be used by clients who want to format something directly and supplying a configuration in memory, and they're not interested in the search logic used by the frontend.

@samdeane
Copy link
Author

samdeane commented Nov 7, 2024

I think the name of the file should be .swift-format-ignore

Yes, makes sense 👍

@samdeane
Copy link
Author

samdeane commented Nov 7, 2024

For the second point, raising this check into the frontend seems more appropriate. Maybe FileIterator should be responsible for looking for the .swift-format-ignore

I think I was fixated on the idea that the ignoring mode needed to be configurable from within a .swift-format, as well as by the presence of a .swift-format-ignore file. Having done it though, I struggle to see the utility of that setting.

I'll try your plan instead.

@samdeane
Copy link
Author

samdeane commented Nov 7, 2024

Ok, I got rid of the setting and moved the check into FileIterator's directory handling.

It works well for input directories, and for subdirectories reached via recursion, but doesn't work for input files (e.g: in the case where individual file paths are specified on the command line directly).

For input files, we need to perform a check for .swift-format-ignore in the same directory as the file. Putting that into the iterator's next() feels wrong as it's not needed in most cases. My inclination is to filter the input urls in init, and perform this test on any that are files.

@allevato
Copy link
Member

allevato commented Nov 7, 2024

For input files, we need to perform a check for .swift-format-ignore in the same directory as the file. Putting that into the iterator's next() feels wrong as it's not needed in most cases. My inclination is to filter the input urls in init, and perform this test on any that are files.

That makes the most sense to me.

@samdeane
Copy link
Author

samdeane commented Nov 7, 2024

Ok, 'tis a little closer now.

One question is what it should do if --configuration <file> is passed in.

The README still says that the filesystem won't be searched; which is true for .swift-format, but no longer true for .swift-format-ignore.

The path of least resistance is to clarify the README, but that depends whether that's the behaviour we want.

Alternatively I'll need to add a flag to FileIterator to indicate whether or not it should check for the ignore file. Which is perfectly doable, of course.

@allevato
Copy link
Member

allevato commented Nov 7, 2024

One question is what it should do if --configuration <file> is passed in.

The README still says that the filesystem won't be searched; which is true for .swift-format, but no longer true for .swift-format-ignore.

The implication of that statement is that the filesystem won't be searched for the configuration (i.e., .swift-format file) specifically. We don't need it to apply to the .swift-format-ignore file.

If we want a way to ignore the ignore file, we could add something like a --force option to the command line interface, but I don't think we need it today.

README.md Outdated
Comment on lines 225 to 226
Note that the contents of any `.swift-format-ignore` are ignored; the
file can be empty.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this further, we may eventually want to allow users to specify patterns here like other tools' ignore files. So we should make sure that the behavior that we support today would upgrade cleanly to something more elaborate in the future. If we allow the file to be empty today and then decide that we want to allow patterns, that becomes an awkward path because an empty file ought to mean "ignore nothing" in that world.

The most obvious thing that comes to mind is for us to currently require that the contents of the .swift-format-ignore file are exactly * (modulo whitespace). Then anyone who wants to ignore a directory could put that in their file and it would continue to work later if we start expanding it to more complex patterns. When we find the file, we could open it and error out if the file contains anything other than *, telling users that that's the only supported option today.

So I would remove this paragraph and change the one you added further up to read something like this instead:

If it finds a file named .swift-format-ignore, its contents will determine which files in that directory should be ignored by swift-format. Currently, the only supported option is * (ignores all files).

@samdeane
Copy link
Author

samdeane commented Nov 7, 2024

If we want a way to ignore the ignore file, we could add something like a --force option to the command line interface, but I don't think we need it today.

Yeah, I was thinking the same. I'll reword the docs.

repeat {
containingDirectory.deleteLastPathComponent()
let candidateFile = containingDirectory.appendingPathComponent(FileIterator.ignoreFileName)
if FileManager.default.isReadableFile(atPath: candidateFile.path) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More than just checking for the presence of the file, I think we go the extra step of validating that it only contains * so that users aren't surprised later when we add more functionality and it subtly changes the behavior based on those contents. If it contains anything else, we should throw an error saying it's the only supported form (and turn that into a nice human-readable error message when we exit).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to upgrade to gitignore like sytanx in the future, should we make the default value **/*?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, * is sufficient because according to the documentation:

  • "An asterisk '*' matches anything except a slash."
  • "If there is a separator at the beginning or middle (or both) of the pattern, [...]. Otherwise the pattern may also match at any level below the .gitignore level."
  • "If there is a separator at the end of the pattern then the pattern will only match directories, otherwise the pattern can match both files and directories."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants