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

Recursive Configs #301

Closed
wants to merge 21 commits into from
Closed

Conversation

scottrhoyt
Copy link
Contributor

Closes #299

Ok, so here is my first stab at the recursive folder configurations. I think that using a recursive folder search method for the entire linting process would greatly optimize nested configurations, but I implemented this using the simplest algorithm with the current linting search method.

Basically, the configuration to use in linting a file is now a function of the file. To find this configuration, we recursively search backwards from directory of the file until we either find the current config file, reach the root of the lint search, or we find a configuration file to merge with. In this initial implementation, merge is simply overriding the config.

NOTE I primarily put the additional code in an extension to avoid exceeding the line limit for the Configuration type, though I suppose it has some encapsulation value to it.

Here are the things that I am still planning to add to this PR:

  • Config option to use nested configs (defaults to false for performance)
  • Refactor initializing Configuration from a yml to be more efficient (it's getting called a lot more)
  • Suppress spamming "Loading configuration from " messages intelligently

Let me know what you think so far and I'll continue adding commits to this PR.

public func queuedPrintError(string: String) {
dispatch_async(outputQueue) {
fputs(string + "\n", stderr)
public func queuedPrintError(string: String, silent: Bool = false) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of a silent parameter that just bypasses the function like this, I'd rather just not call the function in the first place. Could you please revert this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, but because the new code results in a new configuration load for every file under a nested configuration, reverting this will result in dozens of "Loading configuration from " messages when using nested configs. I found this really obfuscates the logs. Another option would be to suppress the logging inside the init function, but that didn't seem right to me. My intention here was to centralize the logging logic in one place so that the logger could decide how to process. Because we wouldn't have to hunt down selective logging in methods, this could potentially ease the transition to more robust logging if needed. So let me know which of the three solutions you'd like:

  1. Revert to original, which is the simplest but logs will be muddled with new load messages
  2. Put the selective logging logic in the init function which requires no change to logging functions, but makes init more complicated and sets the precedent of distributing logging logic to non-logging objects.
  3. Put the selective logging logic in the logger which solves the above problems but results in a degenerative trivial silent logging case

Copy link
Collaborator

Choose a reason for hiding this comment

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

Put the selective logging logic in the init function which requires no change to logging functions, but makes init more complicated and sets the precedent of distributing logging logic to non-logging objects.

This is what I was trying to say. The logging in Configuration's initializer is already unfortunate but in fact, it should be relatively straightforward to move it to the swiftlint CLI app instead, which would address the need to silence some configuration loads without polluting the model object with logging code.

Ideally, all logs would be in swiftlint with none in SwiftLintFramework.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I will revert this and instead put the conditional logging inside of Configuration.init. Stay tuned! :)

@scottrhoyt
Copy link
Contributor Author

Logging is now suppressed in Configuration.init(...).

queuedPrintError("Loading configuration from '\(path)'")
self.init(yaml: yamlContents)!
if let yamlConfig = Configuration.loadYaml(yamlContents) {
if !silent {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 much better IMO

@jpsim
Copy link
Collaborator

jpsim commented Dec 29, 2015

This is looking quite good, @scottrhoyt! Please don't let my many comments discourage you 😉.

It would be nice to have some tests for this. The rest of the project is pretty well tested IMO, so it'd be nice to keep that up here.

It might make sense to introduce a Fixtures/ directory in SwiftLintFrameworkTests/ since much of this functionality relies on directory structure. Otherwise, some straightforward unit tests could be sufficient.

@scottrhoyt
Copy link
Contributor Author

@jpsim No worries. I can definitely sympathize with being a test nut. I wrote some tests for for this in the best way I could come up with. All in all, writing the tests took longer than the code (I hate when that happens), but that was mostly because of the things I needed to support the tests. This is what I did:

  • Enabled code coverage generation for the project
  • Added the isEqual method to the Rule and ParameterizedRule protocols via extensions
  • Wrote conformance for RuleParameter and Configuration to Equatable
  • Wrote tests for all of the above
  • Wrote a test for Configuration.merge
  • Created a mock project structure in the Resources folder of the testing bundle with mock Swift files and mock *.yml's
  • Tested recursive configuration search with the mock project structure.

Let me know what you think.


func == (lhs: [Rule], rhs: [Rule]) -> Bool {
if lhs.count == rhs.count {
return zip(lhs, rhs).map { $0.isEqualTo($1) }.reduce(true) { $0 && $1 }
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you can avoid creating the intermediate closures here:

zip(lhs, rhs).map(Rule.isEqualTo).reduce(true, &&)

- "the place where i commited many coding sins"
line_length: 10000000000
reporter: "json"
use_nested_configs: true
Copy link
Collaborator

Choose a reason for hiding this comment

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

missing trailing newline here

@jpsim
Copy link
Collaborator

jpsim commented Dec 31, 2015

This is looking really good, could you please add an entry to the CHANGELOG following these guidelines and add some basic documentation in the README?

@scottrhoyt
Copy link
Contributor Author

@jpsim CHANGELOG and README updated. Let me know if you need anything else.

* realm/master:
  use SourceKitten 0.7.3 release
  [README] `excluded` overrides `included`
  only generate violations once for triggering examples in verifyRule()
  Support command comment modifiers
  [CHANGELOG] Fix entry
  [CHANGELOG] add entry
  Fix realm#295
  Add failing test to ValidDocsRule.swift
  [CHANGELOG] Fix entry
  test violation locations
  [CHANGELOG] add entry
  Fix realm#294

# Conflicts:
#	CHANGELOG.md
#	README.md
@scottrhoyt
Copy link
Contributor Author

I pulled master into my branch to resolve the conflicts in README and CHANGELOG

@@ -1 +1 @@
Subproject commit 0ed186552d8f9f3e128ab382581102c55c2d3c73
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jpsim might want to make sure this is as intended? Seems to have been switched during merging master.

@jpsim jpsim mentioned this pull request Jan 2, 2016
@jpsim
Copy link
Collaborator

jpsim commented Jan 3, 2016

Many many thanks for this work @scottrhoyt! I made a few tweaks and merged in #307.

@jpsim jpsim closed this Jan 3, 2016
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.

2 participants