Skip to content

Conversation

@badosu
Copy link
Contributor

@badosu badosu commented Dec 5, 2025

Previously whenever there was some error initializing the project node a few things would happen:

  • The node supervisor start code would fail with match error
  • The server would shutdown via init_timeout
  • There would be no indication as to the reason, except reading the node message debug message in expert.log

Now:

  • We properly respond to an initialize request with a server not initialized error
  • We gracefully shut down the node processes
  • We send an error message to the client with the last message sent by the node:
Node exited with status 127, last message: /home/badosu/.local/share/mise/installs/elixir/1.18.4-otp-26/bin/elixir: line 244: exec: erl: not found

This PR is motivated as an attempt to mitigate one of the many situations where expert fails silently, e.g.: #241

Remarks

  • We additionally remove code related to waiting for didChangeConfiguration since it was noop

@badosu badosu force-pushed the badosu/initialize-error branch from d6c6ff4 to c93445d Compare December 5, 2025 22:46
@badosu badosu changed the title feat: Properly respond to and log initialization errors feat: properly respond to and log initialization errors Dec 5, 2025
@badosu badosu force-pushed the badosu/initialize-error branch from c93445d to 3672c0b Compare December 5, 2025 22:49
@badosu badosu force-pushed the badosu/initialize-error branch from 3672c0b to 4e099f7 Compare December 5, 2025 23:04
It can take many seconds until the engine is available, hence editors
might time out the initialization.

We still at least send an error $window/logMessage with the
initialization error when it happens.
@badosu badosu force-pushed the badosu/initialize-error branch from ff0aed9 to dc9c8f1 Compare December 9, 2025 08:04
end

def on_exit_status(%__MODULE__{} = state, exit_status) do
Logger.debug(
Copy link
Member

Choose a reason for hiding this comment

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

instead, lets put an info log in the 0 clause of the case, saying the engine shutdown

then in the error clause, put an error log saying it shutdown unexpectedly

Comment on lines 13 to 17
patch(System, :argv, fn -> ["--port", "0"] end)

assert {:ok, _} = Application.ensure_all_started(:expert)

on_exit(fn -> Application.stop(:expert) end)
Copy link
Member

Choose a reason for hiding this comment

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

there are helpers to in GenLSP already for starting the server with the tcp adapter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, but they start the server individually whereas Expert requires its own constellation of other servers available to operate (hence why we started as an application).

Copy link
Contributor Author

@badosu badosu Dec 9, 2025

Choose a reason for hiding this comment

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

For now I just removed the full expert server test as I'm not fully confident it will work reliably (the application might already have been started on another test). I might send as a separate PR, let me know your thoughts.

As for using GenLSP.Test.server we'd require allowing it to receive options suitable for our server, for example see buffer initialization hardcoded.

Copy link
Contributor Author

@badosu badosu Dec 9, 2025

Choose a reason for hiding this comment

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

I reintroduced full server testing with a different approach.

And removed it again, it seems to not work as well regardless of the approach, might be better suited on a separate PR.

Copy link
Member

Choose a reason for hiding this comment

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

As for using GenLSP.Test.server we'd require allowing it to receive options suitable for our server, for example see buffer initialization hardcoded.

I don't really understand what you are meaning here.

With the GenLSP test sdk, we should be able to start individual expert server instances and test them in isolation. I did it for basically every test in Next LS.

I think there are some small changes to make to make that possible as it stands right now, but can be accomplished later on.

Copy link
Contributor Author

@badosu badosu Dec 9, 2025

Choose a reason for hiding this comment

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

As for using GenLSP.Test.server we'd require allowing it to receive options suitable for our server, for example see buffer initialization hardcoded.

I don't really understand what you are meaning here.

For example, our application starts our genlsp buffer with a name and specific opts: {GenLSP.Buffer, [name: Expert.Buffer] ++ buffer_opts}. Maybe I'm missing something, but this is moot until I can make the full server test reliable anyway.

I think there are some small changes to make to make that possible as it stands right now, but can be accomplished later on.

Happy to hear if they seem straightforward enough.

Copy link
Member

Choose a reason for hiding this comment

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

For example, our application starts our genlsp buffer with a name and specific opts: {GenLSP.Buffer, [name: Expert.Buffer] ++ buffer_opts}. Maybe I'm missing something, but this is moot until I can make the full server test reliable anyway.

The idea is you don't start/utilize the application, you start an instance of the server per test, each has their own buffer and tcp connection to ensure isolation.

When you start the server with the server/2 function in the GenLSP test sdk, it starts a buffer and passes it to the options of your lsp process. The one started in the application supervision tree doesn't really matter.

You can see more [https://github.com/elixir-tools/next-ls/blob/main/test/support/utils.ex#L67]

we can look into this later, tho.

Copy link
Contributor Author

@badosu badosu Dec 10, 2025

Choose a reason for hiding this comment

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

The idea is you don't start/utilize the application, you start an instance of the server per test, each has their own buffer and tcp connection to ensure isolation.

Oh, I completely understand that. In fact I had this iteration and one based on that that didn't start particularly Expert instead using GenLSP.Test.server.

The problem is that for now it seems to me that the implementation of GenLSP.Test.server itself does a few things I'd rather have it not, or at least accept options for those. You can see for example the kind of child_spec I'd want for it to be able to initialize instead here.

We can also vendor it ourselves anyway so this is not a big deal, or maybe even having the special names don't cause any issues.

Again this is a bit moot right now anyway so I won't digress further into it.

Copy link
Member

Choose a reason for hiding this comment

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

Again this is a bit moot right now anyway so I won't digress further into it.

Discussion is fine, its great to get feedback

We can also vendor it ourselves anyway so this is not a big deal,

I'm the author of GenLSP. We can change it however we want.

The problem is that for now it seems to me that the implementation of GenLSP.Test.server itself does a few things I'd rather have it not, or at least accept options for those. You can see for example the kind of child_spec I'd want for it to be able to initialize instead here.

I'm not really following this. It overrides the assigns, buffer, and task queue but passes through any other options.

Those 3 processes should be opaque to the consumer, so you really shouldn't be reading into them from a test anyway, but they are also available from in the return value from the server function.

Why might you want to start those yourself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why might you want to start those yourself?

There might be a circumstance in which the name is explicitly used, for example at least once we explicitly use the :expert_task_supervisor name.

If you assess all is good then all is good 👍

@mhanberg mhanberg changed the title feat: properly respond to and log initialization errors fix: properly log when engine fails to initialize Dec 9, 2025
- Also show errors in the client via window/showMessage
- Improve logging cases
- Only register with nodemapper when node started ok
- For now do not introduce full expert test
@badosu badosu force-pushed the badosu/initialize-error branch 2 times, most recently from 3a79611 to 318d0d6 Compare December 9, 2025 19:37
Now we start manually all application children instead of ensuring the
application has started
@badosu badosu force-pushed the badosu/initialize-error branch from 318d0d6 to e381804 Compare December 9, 2025 19:49
@badosu badosu force-pushed the badosu/initialize-error branch from 21a1b68 to d168107 Compare December 9, 2025 20:22

_error_status ->
Logger.error(
"Engine shut down unexpectedly, node exited with status #{exit_status}). Last message: #{state.last_message}"
Copy link
Member

Choose a reason for hiding this comment

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

Add the root_uri here as well and any other log about the engine

Copy link
Contributor Author

@badosu badosu Dec 10, 2025

Choose a reason for hiding this comment

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

I added the project as a prefix but I'm not sure that's desired in the current incarnation. Let me know if this message format looks good to you.

@badosu badosu force-pushed the badosu/initialize-error branch from 415c190 to 5ae550e Compare December 10, 2025 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants