-
-
Notifications
You must be signed in to change notification settings - Fork 33
[fix] Determine if completion is displayable based on the project dependencies #9
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
Conversation
I think this approach misses Note 1 about application controller: elixir compiler does not update entries there on recompilation. |
@lukaszsamson another alternative I see is to read the 29d61f0 implements the approach I mention here. I think we might also be able to cache those, but if we have as-you-time recompilation I'd have to measure to know if it's really worth it |
2b53730
to
5ef3103
Compare
[ | ||
# {:dep_from_hexpm, "~> 0.3.0"}, | ||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} | ||
{:stream_data, "~> 1.2"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a test that checked that a SreamData module was being completed. It worked before because we only checked if the module wasn't a Lexical one, but with the new approach that checks if module is part of the project, we actually need the dependency to be installed.
I've been having quite a lot of bad luck with the CI on a flaky test here |
# we consider it a "project module" if: | ||
# 1. the module is in the project apps | ||
# 2. there is no module app and there is not a project module | ||
# (this is the case for some test completions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively we can make the tests correctly recognize all project defined modules as part of the project application, still getting familiarized with how this happens
defp get_project_apps(project) do | ||
build_path = RemoteControl.Build.path(project) | ||
|
||
for app <- File.ls!(Path.join([build_path, "test", "lib"])), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming here that by the time we try to get completions, RemoteControl is already started and the project is already compiled. I haven't found issues using a build of this PR for a while, but it's still an assumption.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function should be pushed into remote_control
since it's in control of the project and what's going on. I think that doing operations inside of remote control will allow you to leverage the mix project.
Having this logic in the server app was, i think, a mistake.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that doing operations inside of remote control will allow you to leverage the mix project.
Yes and no; Mix does not always produce the list of project applications. I'm not sure why exactly, but I would often get an empty list of applications back from Mix. It would also, with current Mix.Project
apis, miss both the project and the dependencies extra applications.
Loading the .app
files doesn't require an rpc call to the remote control node, but I see the point of keeping project related logic in the remote control app, I can move this code to that app
module | ||
end | ||
|
||
def to_atom(module_string) when is_binary(module_string) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this safe? what happens if we pass in :some_module
here?
def project_apps(project) do | ||
build_path = RemoteControl.Build.path(project) | ||
|
||
for app <- File.ls!(Path.join([build_path, "test", "lib"])), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we not use Mix.Project
here to do this work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consulting the filesystem on every module completion will cost a lot of performance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we not use
Mix.Project
here to do this work?
I tried, it doesn't yield good results. As an example, I just tried this on an igniter
clone and I get these results from the RemoteControl node:
Mix.Project.apps_paths: nil
Mix.Project.deps_apps: []
Running completions a few times I eventually get this:
Mix.Project.apps_paths: nil
Mix.Project.deps_apps: [:sourceror, :earmark_parser, :text_diff, :file_system, :deep_merge, :decimal, :spitfire, :eflame, :mime, :nimble_options, :nimble_parsec, :bunt, :telemetry, :ex_check, :jason, :doctor, :statistex, :mix_test_watch, :ham, :hpax, :mint, :yamerl, :yaml_elixir, :mix_audit, :glob_ex, :credo, :phx_new, :makeup, :makeup_elixir, :makeup_erlang, :ex_doc, :inflex, :erlex, :dialyxir, :nimble_pool, :finch, :req, :rewrite, :benchee, :owl, :mimic]
Which is close, but is missing :logger
, :public_key
, :ssl
, :inets
, some of which are loaded by elixir, some of which were defined as extra_applications
in one of the mix files. As far as I can tell, there is no public Mix api to get those, so we need to resort to reading manifests in the filesystem to get everything(which is close to what mix does anyways after compilation).
Also, adding a dependency causes Mix.deps_apps
to return []
again for a while until it catches up.
consulting the filesystem on every module completion will cost a lot of performance
I was mulling over this too, I ultimately decided to fetch the project applications after compilation, and keep them in memory for completions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the missing apps are all in erlang though, and should be available for completions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good call, I added the builtin apps from both erlang and elixir in acc6565. Short of an api to list them, I had to hardcode them.
module_app in project_apps or (is_nil(module_app) and is_nil(project.project_module)) or | ||
(not is_nil(module_app) and module_app == project_app) or | ||
(is_nil(module_app) and not is_nil(project.project_module) and | ||
module == project.project_module) or | ||
(is_nil(metadata) or result_app in project_apps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would pushing this into the remote control app help things?
Also, i think a cond would be a lot more readable than this very nested statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would pushing this into the remote control app help things?
No, there is something in the test setup that is preventing the application controller from recognizing the Project fixture modules as part of the :project
application, I'm having a bit of a hard time figuring out what is it exactly.
Also, i think a cond would be a lot more readable than this very nested statement.
Updated in 0ff39e9
4b94717
to
0ff39e9
Compare
|
||
defdelegate workspace_symbols(query), to: CodeIntelligence.Symbols, as: :for_workspace | ||
|
||
defdelegate list_apps(), to: RemoteControl.Build.ApplicationCache, as: :list_apps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the as:
necessary here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying without it seems to work, so no
This is an old commit though
3f78e5a
to
34e1a6d
Compare
484dc3d
to
a5d9989
Compare
a5d9989
to
3a47058
Compare
This has been working as expected in my testing for quite a while, and I need the changes to namespacing in this PR for the burrito packaging work, so I'm merging it |
Fixes #8
This shifts the approach from "is this suggestion part of RemoteControl", which requires quite a lot of special casing, to "is this suggestion part of the project" instead.
The approach here is to:
I think we should generally avoid doing string checks on module names to determine where they beling to, since a project may define modules in the same namespace as some other project. For example, if you are editing a different project/application that defines modules in the
Lexical
namespace: you'd want completions for the modules you define but not for the ones inLexical.RemoteControl
.For
2
, which is the hardest part here, I decided to parse the.app
files in the build directory. The rationale here is that:What I experienced with other options was:
mix.lock
files include dependencies, but notextra_applications
or applications loaded by elixir by defaultMix.Project.deps_apps()
is sometimes[]
or out of date, which seemed quite unreliable:application_controller.which_applications()
, besides apparently being private API, also include RemoteControl dependencies, applications, and namespaced applicationsThe downside is that we need to parse the
.app
files a few times whenever we have a bunch of suggestions to check.