ocaml-lsp
ocaml-lsp copied to clipboard
Automatically configure/build dune-based project on LSP initialisation
Thanks for developing such a great LSP server that works out of the box. it helped me learn ocaml as I make progress with projects at work!
As a developer using ocaml-lsp on a dune-based project, I would like to open the project in my editor, start lsp and have ocaml-lsp perform necessary dune project configuration/initial building as part of LSP initialisation.
This will lower the barriers to entry for ocaml beginners and strengthen the status of dune as the main build system for ocaml.
I appreciate some dune builds might take a long time, but since a dune build is necessary for intellisense, I still feel it's best that this initial build/config takes place under ocaml-lsp (with a progress report using this)
Prior art/existing LSP servers
rust-analyzer (the lsp server for rust) takes care of project configuration as documented here https://rust-analyzer.github.io/manual.html#configuration
Looking at the call responsible for it, the call hierarchy is below
▾ build_command fn build_command(config: &CargoConfig) -> Command
▾ run pub(crate) fn run(config: &CargoConfig, workspace: &CargoWorkspace, progress: &dyn Fn(String)) -> io::Result<WorkspaceBuildScripts>
▾ run_build_scripts pub fn run_build_scripts(&self, config: &CargoConfig, progress: &dyn Fn(String)) -> Result<WorkspaceBuildScripts>
▾ fetch_build_data pub(crate) fn fetch_build_data(&mut self, cause: Cause)
where fetch_build_data is here https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/reload.rs#L164-L183
Possibly related to this milestone
https://github.com/ocaml/ocaml-lsp/projects/2
Thanks for the interest. Your idea is sound and it's certainly the way we want to go to reduce user facing complexity. I'm just not sure how to handle the transition and manage user expectations when moving to LSP running dune. I.e. what happens when users run dune manually?
What if we have the following experience:
- When OLSP starts, it sends a show message request saying "Do you want to launch dune build in watch mode for better editor experience? All build errors and warnings will be reported in the editor (as diagnostics)" with options "Yes", "No", "Never" (and maybe "Always", "Not in this session" (which would last until olsp terminates)) 1.1. In case of No and Never, user keeps the current workflow of olsp-dune interaction 1.2. If yes, olsp launches dune in watch mode. It's important for olsp to have commands to restart, stop, and start the dune build in watch mode though because I sometimes encounter races during concurrent dune builds and have to stop the dune in watch mode, run the dune command I want, and then run the watch mode again. 1.2.1. Olsp can be smarter by offering to restart dune in watch mode, if it has been first stopped, and another dune command was launched and has terminated.
Cons of this approach:
- User is bothered with a message proposing to launch something, which is less user-friendly
Pros:
- The dune build watch doesn't happen implicitly/magically, so the user isn't caught off-guard
- User has more control over their workflow
Scala Metals also uses compiler builds for intellisense, so we could look into how they approach this problem.
I think that this should wait until running multiple dune commands concurrently doesn't cause dune errors. Asking users to figure out that they need to stop the lsp dune build so they can run dune exec
is rough UX.
Thanks for the additional input.
I'd like to avoid prompts as much as possible. We should do whatever works for the majority of users and offer workarounds or configuration for the rest.
We don't need to necessarily worry about restarting dune. We can borrow the concept of a goal alias from janestreet. The idea would need to be adopted to our environment, but I'll give a rough idea of how it works here. It means running dune in the background with a command similar to dune build @goal -w
and defining the goal alias in some fixed dune file:
(alias
(name goal)
(deps (alias_rec check)))
The user then can interact with the currently running dune by editing the goal alias in some predefined dune
file. For example, to execute a test, runtest
would be added to the goal for the corresponding directory containing the test.