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

feat: auto-discover config file #13313

Merged
merged 24 commits into from
Jan 18, 2022
Merged

feat: auto-discover config file #13313

merged 24 commits into from
Jan 18, 2022

Conversation

ry
Copy link
Member

@ry ry commented Jan 8, 2022

No description provided.

@ry ry requested a review from dsherret as a code owner January 8, 2022 19:09
Comment on lines 169 to 173
for f in flags.config_path_args() {
if let Some(cf) = discover_inner(&f, &mut checked)? {
return Ok(Some(cf));
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit unexpected - IIRC the algorithm we agreed upon was supposed to find a config file for each of the passed arguments, not a config file that was found first. Could you clarify this @ry?

Copy link
Member

@dsherret dsherret Jan 10, 2022

Choose a reason for hiding this comment

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

I've been thinking about this more just now and I think this goes beyond what we've discussed in the past. I'm wondering if when running deno fmt or deno lint should we have deno.json files in sub directories be resepected when there's a deno.json in an ancestor directory.

For example, say you have:

sub_dir
  - deno.json
  - file.ts
deno.json
  1. Should running deno fmt have the same behaviour on file.ts as running deno fmt sub_dir/file.ts?
  2. Should running deno fmt sub_dir/file.ts have the same behaviour as running (cd sub_dir && deno fmt file.ts)?

I can think of a few options:

Option 1: Resolve based on closest deno.json from file path

This option would mean for deno fmt and deno lint along with deno fmt <file-path>, it would always resolve a config file based on the closest deno.json. So includes/excludes in parent deno.jsons would be ignored.

  • Fmt/lint option resolution: Each config file resolves from the default options
  • Includes/excludes: Each config file resolves from the default options
  • Run options: Resolved from closest ancestor deno.json (Same as Option 3)

Personally, I think this is the best option and is the least complex for users to understand. It answers yes to both the questions I asked above.

Option 2: Resolve using resolution from all deno.json files in ancestor directories from file path

This would require resolving all deno.json files in all ancestor directories, then use that information to figure out the fmt/lint options and includes/excludes for a folder based on the combined result of all config files.

  • Fmt/lint option resolution: Each config file resolves from the ancestor directory's options
  • Includes/excludes: Each config file resolves from the ancestor directory's includes/excludes and overrides it.
  • Run options: Requires resolution of each ancestor config and then a resolution to the final options by combining configs.

This would be complex to implement. It answers yes to both the questions I asked above.

Option 3: Use config file resolved for root of each disjointed tree

By "disjointed tree" I mean, directory sub trees that share no common directories from the root with other directory trees. Maybe there is a better word?

This would resolve the config file for each disjointed tree. So deno fmt would resolve based on the cwd and use that, but deno fmt ./child/mod.ts ../sibling/mod.ts ../sibling/other.ts would resolve the config file for ./child and ../sibling to use for their respective files.

  • Fmt/lint option resolution: Each config file resolves from the root of each disjointed tree
  • Includes/excludes: Each config file resolves from the root of each disjointed tree
  • Run options: Resolved from closest ancestor deno.json (same as Option 1)

I think this is the solution we agreed upon before? It answers no to both of the questions I asked above.

Copy link
Member Author

Choose a reason for hiding this comment

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

Option 1 and Option 3 are the same except applying to multiple arguments. I agree with this approach.

Or we can just find the deno.json based on the first argument and if other files have a different config, erroniously ignore it for the time being. These are corner cases that we should have tests for anyway - we can move to option 1/3 in the future.

Copy link
Member

Choose a reason for hiding this comment

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

I believe Option 3 was exactly what we discussed previously.

@kitsonk
Copy link
Contributor

kitsonk commented Jan 10, 2022

Is this shippable without addressing the language server? Feels like it should be included in this PR.

@ry
Copy link
Member Author

ry commented Jan 10, 2022

Is this shippable without addressing the language server? Feels like it should be included in this PR.

What needs to be addressed in the language server?

@kitsonk
Copy link
Contributor

kitsonk commented Jan 10, 2022

Currently the lsp requires an explicit option of "config" to be set in the client configuration for a configuration file to apply:

deno/cli/lsp/config.rs

Lines 157 to 159 in c487b7e

/// An option that points to a path string of the config file to apply to
/// code within the workspace.
pub config: Option<String>,

And when there is something impacted in the environment, like when the client configuration or the configuration file iteself changes, then the language server processes an update:

fn update_config_file(&mut self) -> Result<(), AnyError> {

If we have automatic behaviour to identify the file when deno run, then it only seem logical that the behaviour would be similar by default for the language server, not requiring explicit configuration, which mirrors the command line today, and I would assume that delivery of that functionality would be part of this feature, since a fair amount of people utilise the language server.

Generally this should be fairly straight forward with just a "workspace" and looking in the "workspace root", but when you have "workspace folders" (multi-root workspaces), I suspect it could get a bit convoluted.

Side note, I just realised we are only watching JSON files in the workspace (and not JSONC):

let watch_registration_options =
DidChangeWatchedFilesRegistrationOptions {
watchers: vec![FileSystemWatcher {
glob_pattern: "**/*.json".to_string(),
kind: Some(WatchKind::Change),
}],
};

And not watching for changes in .jsonc files, but if a configuration file is identified, it can be registered for changes, I believe.

Another discussion at one point, which could be delivered later, is that if we automatically detect a deno.json/deno.jsonc it would imply that the project/workspace was implicitly Deno "enabled" not requiring explicit enablement as it does today.

@ry
Copy link
Member Author

ry commented Jan 10, 2022

Could we just rip out the LSP config option? Seems like if we have auto-discover then maybe this option isn't necessary

@kitsonk
Copy link
Contributor

kitsonk commented Jan 10, 2022

Will the CLI still allow --config to be explicitly specified? If so, then you need a similar capability in the configuration of the language server.

@ry ry requested a review from kitsonk as a code owner January 11, 2022 00:27
@ry
Copy link
Member Author

ry commented Jan 15, 2022

@kitsonk I put up a little test case but it doesn't seem to be working. I'm seeing this error message from the LSP

Error retrieving settings for specifier (file:https:///var/folders/9v/kys6gqns6kl8nksyn4l1f9v40000gn/T/.tmpxnaESN/ignored.ts): Parse error: invalid type: map, expected a sequence

Any advice would be appreciated

@kitsonk
Copy link
Contributor

kitsonk commented Jan 15, 2022

Ah, OK, I will have to trace it through, but what is happening, versus the other tests like this, is that it is triggering the workspace folder logic which request from the client the specific configuration for a file, and for some reason it is causing this test to fail.

@bartlomieju
Copy link
Member

Tried this PR on deno_std, works nicely :)

)
.unwrap();

let workspace_root = temp_dir.path().canonicalize().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

This was the fix, in that auto-discovery automatically canonicalizes the discovered path.

An open question though is how do clients behave? Do they canonicalize files? There isn't an easy way to determine. We may need to fix exclude and include properties for things like format and lint to canoncilize files... 😕

@kitsonk
Copy link
Contributor

kitsonk commented Jan 17, 2022

Looks like something is hanging with lsp_diagnostics_refresh_dependents...

@kitsonk
Copy link
Contributor

kitsonk commented Jan 17, 2022

Looks like something is hanging with lsp_diagnostics_refresh_dependents...

Found the problem, will push a fix after I check the rest of the tests...

Copy link
Contributor

@kitsonk kitsonk left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

LGTM too.

There are some unrelated changes in this PR, but I'm not gonna nitpick.

Copy link
Member

@dsherret dsherret left a comment

Choose a reason for hiding this comment

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

LGTM too!

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

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

Tried it out locally, and it is working great. LGTM!

@jsejcksn
Copy link
Contributor

jsejcksn commented Jan 21, 2022

I don't see anything in the release notes about how to disable this feature. How can I prevent deno from walking the directory hierarchy and discovering configs?

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.

None yet

6 participants