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

Introduce references tracer #80

Closed

Conversation

lukaszsamson
Copy link
Collaborator

Addresses #75

Copy link
Member

@axelson axelson 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 starting the work on this! I have a discussion point about the overall interface and direction.

pos_integer,
ElixirSense.Core.References.Tracer.call_trace_t() | nil
) :: [References.reference_info()]
def references(code, line, column, trace \\ nil) do
Copy link
Member

Choose a reason for hiding this comment

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

I think that requiring passing in a trace (that was generated by ElixirSense.Core.References.Tracer which isn't marked private, although it probably should be) to a public api could be improved because now callers of ElixirSense.references/4 need to call a potentially private api.

Instead perhaps we should collect and store the compiler trace within ElixirSense itself, although that would require ElixirSense to have it's own supervision hierarchy. Any thoughts on the tradeoffs involved?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Obviously the tracer API needs to be public.

Regarding setting up a supervision hierarchy in ElixirSense, it would be a major breaking change for the users. ElixirSense is currently a stateless library. For it to work It requires the client to compile and load all the modules, as it is in the case of elixir-LS. In my opinion it is easier to setup and manage the tracer process there.

Copy link
Member

Choose a reason for hiding this comment

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

Well changing ElixirSense to have it's own supervision hierarchy should generally be transparent to end-users, although I can see an argument that managing the tracer process would be easier from ElixirLS 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @axelson, ElixirSense could have its own supervision tree that end-users would not have to know about, especially since ElixirSense is largely obscured behind ElixirLS. Supervision trees are simple to set up, and I could make a PR to add just that to ElixirSense (that I imagine would just supervise the TCP server at this stage).

Alternatively, could we use :ets for storage of the traces and not have to manually manage supervision?

Wandering perhaps too far into the philosophical; Is there a reason other than historical for why ElixirSense and ElixirLS are separate codebases? Wouldn't changes like these be easier to think about if they were unified?

Copy link
Collaborator

@msaraiva msaraiva left a comment

Choose a reason for hiding this comment

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

@lukaszsamson this is fantastic work! The only thing I think we should do differently is to move any version dependent code to the core/normalized folder. Anything that has different implementations for different version should be isolated and kept there. It makes easier for us to keep track of those differences and, when the time comes, decide how to deprecate/remove support for older versions.

So, maybe we could have something like ElixirSense.Core.Normalized.References.calls(...) with the minimum non-generic code. You could even don't expose the tracer to the public API for now and initialize/use it internally inside that function. As far as I could see we can only have a single named tracer process at a time and we don't have concurrent calls so we should be fine with this approach for now.

In the future, as the elixir core team adds more features to the tracer, we can consider replacing our parser with a much simpler version that relies on the tracer. Then we can review the whole approach. WDYT?

@lukaszsamson
Copy link
Collaborator Author

@msaraiva I fully agree on moving environment dependant code to ElixirSense.Core.Normalized. I'm not sure though if we can hide the tracer process completely from the public API. Anyways, I'll give it a try and see how it goes.

@axelson
Copy link
Member

axelson commented Sep 22, 2020

I wanted to make a quick note here that in addition to storing the trace in memory, we will want to store it on disk as well. That way every time that ElixirLS starts up we don't need to clear and recompile the user's entire code base. Boundary is the best code base to look at for an example of this. Here is the line where the manifest is read back from a file: https://github.com/sasa1977/boundary/blob/b3ddb21a8ff3fb9da6c6f7d4927f291a6c985a2e/lib/boundary/mix/xref.ex#L73

@J3RN
Copy link
Contributor

J3RN commented Sep 23, 2020

@axelson Should we account for the possibility that the codebase was changed while ElixirLS was not running? In that case, the manifest written to disk will be stale. It would be nice if Elixir provided some kind of compilation hash.

@axelson
Copy link
Member

axelson commented Sep 24, 2020

Yes that is an important consideration, not sure off the top of my head how to best account for that

@lukaszsamson
Copy link
Collaborator Author

Tracers are broken in elixir 1.11.1 (not sure about 1.11.0) - see elixir-lang/elixir#10451

Copy link
Contributor

@J3RN J3RN left a comment

Choose a reason for hiding this comment

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

Since this has been open for a while, I thought I would try to rekindle interest with a few suggestions and insights.

Not strictly relevant to this PR, but it occurred to me that we could have multiple reference "providers". i.e. There would be a ElixirSense.References behaviour that could be implemented by one or more modules. With such a system, we could have both a references implementation built on Mix.Tasks.Xref for backwards compatibility and another build on compilation tracers for future compatibility.

pos_integer,
ElixirSense.Core.References.Tracer.call_trace_t() | nil
) :: [References.reference_info()]
def references(code, line, column, trace \\ nil) do
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @axelson, ElixirSense could have its own supervision tree that end-users would not have to know about, especially since ElixirSense is largely obscured behind ElixirLS. Supervision trees are simple to set up, and I could make a PR to add just that to ElixirSense (that I imagine would just supervise the TCP server at this stage).

Alternatively, could we use :ets for storage of the traces and not have to manually manage supervision?

Wandering perhaps too far into the philosophical; Is there a reason other than historical for why ElixirSense and ElixirLS are separate codebases? Wouldn't changes like these be easier to think about if they were unified?

})

:ok
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we also need a trace function head for {:local_function, ...} for local (in-module) function calls?

@lukaszsamson lukaszsamson marked this pull request as draft January 26, 2021 08:48
@msaraiva
Copy link
Collaborator

@lukaszsamson since we're removing the built-in server and probably other stuff that is not necessary anymore, maybe it's time to consider dropping support for versions < v1.10.0 so we could start using the tracers everywhere we can. After all, Elixir v1.12.0 has been officially released so supporting the three last minor versions seems to be already a fantastic achievement. Doing that will probably reduce the complexity of many features and make the lib more stable. Personally, I would even go further and keep support only for versions >= v1.11.0, in case that would also help to remove code.

axelson added a commit to axelson/elixir-ls that referenced this pull request Jun 26, 2021
Keeps them in line with our Version Support Guidance:
https://github.com/elixir-lsp/elixir-ls/blob/be0af9dadb2c4cceeb0893fe71d8380debe33f08/DEVELOPMENT.md#version-support-guidelines

Also by moving the minimum version of Elixir to 1.10 we can finally use
compilation tracers (initial PR at elixir-lsp/elixir_sense#80)
@axelson
Copy link
Member

axelson commented Jun 26, 2021

FYI, I just opened elixir-lsp/elixir-ls#561 to drop support for Elixir < 1.10.0 (as per the ElixirLS Version Support Guidelines: https://github.com/elixir-lsp/elixir-ls/blob/206240a5d3a116ab84ef1abf16bee4512ac16381/DEVELOPMENT.md#version-support-guidelines)

So once that is merged I think we can consider this unblocked and we can move forward on it.

axelson added a commit to elixir-lsp/elixir-ls that referenced this pull request Jun 27, 2021
* Update minimum versions to Elixir 1.10 and OTP 22

Keeps them in line with our Version Support Guidance:
https://github.com/elixir-lsp/elixir-ls/blob/be0af9dadb2c4cceeb0893fe71d8380debe33f08/DEVELOPMENT.md#version-support-guidelines

Also by moving the minimum version of Elixir to 1.10 we can finally use
compilation tracers (initial PR at elixir-lsp/elixir_sense#80)

* Fix formatting
vanjabucic pushed a commit to vanjabucic/elixir-ls that referenced this pull request Jul 5, 2021
* Update minimum versions to Elixir 1.10 and OTP 22

Keeps them in line with our Version Support Guidance:
https://github.com/elixir-lsp/elixir-ls/blob/be0af9dadb2c4cceeb0893fe71d8380debe33f08/DEVELOPMENT.md#version-support-guidelines

Also by moving the minimum version of Elixir to 1.10 we can finally use
compilation tracers (initial PR at elixir-lsp/elixir_sense#80)

* Fix formatting
@lukaszsamson
Copy link
Collaborator Author

Since we dropped support for anything older than 1.10 this can now be addressed

@lukaszsamson lukaszsamson marked this pull request as ready for review July 10, 2022 20:34
@lukaszsamson
Copy link
Collaborator Author

lukaszsamson commented Jul 10, 2022

Closing. I opened an new one as github for some reason is not picking up changes in the original branch. Tracked in #160

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

4 participants