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

Configurable Unix system command #1585

Merged
merged 2 commits into from May 10, 2023

Conversation

3Rafal
Copy link
Collaborator

@3Rafal 3Rafal commented Mar 27, 2023

As described in ocaml/ocaml-lsp#1052

There was already a discussion about introducing Spawn dependency to replace Sys.command here: #1420

@3Rafal 3Rafal force-pushed the unix-merlin-system-command branch from b7890c0 to f6ad8d8 Compare March 27, 2023 09:52
@3Rafal 3Rafal marked this pull request as draft March 27, 2023 13:40
@3Rafal
Copy link
Collaborator Author

3Rafal commented Mar 27, 2023

@rgrinberg , can you specify the ocaml-lsp cases in which this patch is necessary?
We're changing Sys.command which uses system() call, for a Spawn.spawn.
As I understand it, both solutions rely on fork/exec.

@rgrinberg
Copy link
Member

Note that it's not about avoiding fork/exec (although i'll take it when it's possible). It's for:

  1. Having access to the spawned pid for cancellation
  2. Avoiding the shell and not relying on it for stdin/stdout/stderr

@rgrinberg
Copy link
Member

Btw, this fix isn't bringing us to an ideal long term situation. We don't want merlin imposing on lsp on how to lunch processes, nor do we want lsp imposing on merlin in the same way. Something closer to what we want would be an API for running preprocessors. E.g:

val pp : cwd:string -> prog:string -> args:string list -> (string, int) result

@voodoos
Copy link
Collaborator

voodoos commented Mar 27, 2023

Btw, this fix isn't bringing us to an ideal long term situation. We don't want merlin imposing on lsp on how to lunch processes, nor do we want lsp imposing on merlin in the same way. Something closer to what we want would be an API for running preprocessors. E.g:

val pp : cwd:string -> prog:string -> args:string list -> (string, int) result

We could make it customizable in https://github.com/ocaml/merlin/blob/master/src/utils/lib_config.mli

It's not the most elegant solution but it might provide the flexibility we need until a more involved refactor of "merlin-as-a-lib" is done.

@3Rafal
Copy link
Collaborator Author

3Rafal commented Mar 27, 2023

@rgrinberg , I'm not sure that I follow.

If we create the API with following function:

val pp : cwd:string -> prog:string -> args:string list -> (string, int) result

then how will we handle cancelation? I assume that returned int stands for exit code and PID is not exposed.

@rgrinberg
Copy link
Member

The implementation of that function on the lsp side would register the spawned pid somewhere and then kill it once cancellation is called. By the way, the error code should be a bit more accurate:

val pp : cwd:string -> prog:string -> args:string list -> (string, [`Finish of int | `Cancelled ]) result

@3Rafal 3Rafal force-pushed the unix-merlin-system-command branch from 6a4e78d to a8c8e9c Compare March 30, 2023 10:06
@3Rafal 3Rafal marked this pull request as ready for review March 30, 2023 10:09
@3Rafal
Copy link
Collaborator Author

3Rafal commented Mar 30, 2023

@voodoos , @rgrinberg
I have created an extension point that fits the "standard" Merlin implementation and the one proposed by Rudi.
The interface type is simplified and mimicked after Sys.command. I think it provides enough flexibility for ocaml-lsp to implement a cancelation mechanism, and does not require significant changes on Merlin part.

@3Rafal 3Rafal changed the title Refactor Unix merlin_system_command Configurable Unix system command Mar 30, 2023
@voodoos
Copy link
Collaborator

voodoos commented Mar 30, 2023

Looks good to me !

@3Rafal
Copy link
Collaborator Author

3Rafal commented Mar 30, 2023

Ok, I'll just rebase it on current main branch and provide a changelog :-)

@voodoos
Copy link
Collaborator

voodoos commented Mar 30, 2023

Ok, I'll just rebase it on current main branch and provide a changelog :-)

👍 also we will wait for @rgrinberg double-check before merging :-)

@rgrinberg
Copy link
Member

This interface is problematic because it relies on the shell to run the pp/ppx commands. We want the drop the dependency on the shell whenever possible.

Personally, I would prefer an API that is even higher level. For example:

val pp : ast:string -> [`Impl | `Intf] -> source_path:string -> prog:string -> args:string list -> [`Pp of string | `Marshal of string]

Also I find the reliance on error code to signal errors to be a poor choice as well.

I suppose it's good enough to solve the immediate problem, but I wouldn't want to be stuck with this interface.

Is there a reason why we're in a rush to fix this issue?

@3Rafal
Copy link
Collaborator Author

3Rafal commented Mar 31, 2023

@rgrinberg

Is there a reason why we're in a rush to fix this issue?

The primary reason for prioritizing this issue is the potential for reducing maintenance costs on both projects. By upstreaming the merlin fork, we can simplify the overall development process and minimize duplicated efforts.

Moreover, this issue has been identified as a significant factor blocking adoption for some users in the OCaml community.

The proposed Pull Request offers a quick and efficient way to upstream this patch. This solution is not the final version. We intend to further refine the integration between ocaml-lsp and merlin once the initial version has been upstreamed.

@3Rafal 3Rafal force-pushed the unix-merlin-system-command branch from fa6cdd6 to 9c5676b Compare April 3, 2023 14:55
@voodoos
Copy link
Collaborator

voodoos commented Apr 3, 2023

@rgrinberg, about the return type: (string, [`Finish of int | `Cancelled ]) result:

Adding a `Canceled case without properly handling it on Merlin's side wouldn't make much sense to me.
Right now, if a PPX fails Merlin simply keeps going down the pipeline with the non-preprocessed source.

We should at some point (and probably sooner than later) add cancellation support to the Mpipeline itself, but I think this is out-of-scope for this PR that really simply aims to provide enough flexibility to support current ocaml-lsp usage without additional patches.

@rgrinberg
Copy link
Member

rgrinberg commented Apr 3, 2023

Sure, I was talking about the ideal API that we should be looking towards. Of course merlin should handle cancellation in a smarter way than just continuing with the unpreprocessed ast.

@voodoos
Copy link
Collaborator

voodoos commented Apr 11, 2023

@3Rafal could you add a changelog entry ? I will try to review by the end of the week.

@voodoos voodoos added this to Planned for backporting in Backport to 500 via automation Apr 11, 2023
@voodoos voodoos self-requested a review April 11, 2023 17:51
3Rafal added a commit to 3Rafal/merlin that referenced this pull request Apr 12, 2023
@3Rafal 3Rafal force-pushed the unix-merlin-system-command branch from 9c5676b to 2f23f0d Compare April 12, 2023 11:39
@3Rafal
Copy link
Collaborator Author

3Rafal commented Apr 12, 2023

@voodoos , done. :)

src/ocaml/driver/pparse.ml Outdated Show resolved Hide resolved
src/ocaml/driver/pparse.ml Outdated Show resolved Hide resolved
src/ocaml/driver/pparse.ml Outdated Show resolved Hide resolved
src/utils/std.ml Outdated Show resolved Hide resolved
@voodoos
Copy link
Collaborator

voodoos commented Apr 27, 2023

I had a go at the latest changes we discussed with Rafal. This lead me to (re)-discover the source of our difficulties: in configuration, a ppx and its arguments are passed in one block to Merlin which makes it hard to distinguished the program from it's arguments. Additionally, when the .merlin file has been hand-crafted it can rely on various different quoting styles and even shell expansions / embedded commands. (free visit to the zoo: https://github.com/search?q=path%3A%22.merlin%22+FLG+-ppx&type=code&p=1).

That means that unless we decide to parse these and unquote them (which is something we really don't want to do) there is no way for us to use sane quotation functions like Filename.quote_command, or drop the call to the shell, if we want to ensure Merlin compatibility with the whole Zoo.

I documented the hook exposed in Lib_config with all that should be taken care of by implementers:

(** [set_run_in_directory] sets an implementation for spawning external
programs. This is used by Merlin to spawn preprocessors and ppxes. For
compatibility reasons, there are currently some limitations to how this
should be implemented:
- Implementation should expect [prog] to be already quoted and contain
arguments. This is due to how ppx configuration is passed to Merlin. In
order to prepare a future transition to more sane argument passing, the
implementation can look at the [prog_is_quoted] argument to know if it
is actually safe to quote the command normally (using
[Filename.quote_command] for example).
- [prog] might contain shell expansions, command substitutions etc. It
should therefore be ran under a shell for maximum compatibility. However
this should never happen when the configuration is generated by Dune.
- Programs runned by this function should never output on stdout since it
is the channel used by Merlin to communicate with the editor. One way to
enforce that is to redirect stdout to stderr.
- As of today Merlin handles the [`Cancelled] return case identically as
other error codes. *)
val set_run_in_directory
: (prog:string
-> prog_is_quoted:bool
-> args:string list
-> cwd:string
-> ?stdin:string
-> ?stdout:string
-> ?stderr:string
-> unit
-> [ `Finished of int | `Cancelled ])
-> unit

@rgrinberg We should at some point add a new directive to the configuration protocol to finally pass ppx configuration in a clean way when using Dune.

voodoos pushed a commit to voodoos/merlin that referenced this pull request Apr 28, 2023
3Rafal and others added 2 commits May 10, 2023 16:12
This is meant for tools using Merlin as a lib to customize the way proprocessors
and ppxes are spawed.

Provided implementations must respect a precise set of constraints.

Author:    Rafal Gwozdzinski <rafal.gwozdzinski@gmail.com>
Co-authored-by:    Ulysse Gérard <thevoodoos@gmail.com>
@voodoos voodoos force-pushed the unix-merlin-system-command branch from 5d09af9 to 8446560 Compare May 10, 2023 14:18
@voodoos voodoos merged commit 31d1bda into ocaml:master May 10, 2023
5 checks passed
Backport to 500 automation moved this from Planned for backporting to Ready for backporting May 10, 2023
@voodoos voodoos moved this from Ready for backporting to 4.9-500 in Backport to 500 May 26, 2023
voodoos added a commit to voodoos/merlin that referenced this pull request May 26, 2023
from 3Rafal/unix-merlin-system-command
voodoos added a commit to voodoos/opam-repository that referenced this pull request May 26, 2023
CHANGES:

Fri May 26 15:23:42 CEST 2023

  + merlin binary
    - Allow monadic IO in dot protocol (ocaml/merlin#1581)
    - Add a `scope` option to the `occurrences` command in preparation for
      the upcoming `project-wide-occurrences` feature (ocaml/merlin#1596)
    - Construct bool-typed holes as `false` instead of `true` in the
      `construct` command, for consistency (ocaml/merlin#1599).
    - Add a hook to configure system command for spawning ppxes when Merlin is
      used as a library. (ocaml/merlin#1585)
    - Implement an all-or-nothing cache for the PPX phase (ocaml/merlin#1584)
    - Cleanup functors caches when backtracking, to avoid memory leaks
      (ocaml/merlin#1609, fixes ocaml/merlin#1529 and ocaml-lsp#1032)
    - Fix `construct` results ordering for sum types sand poly variants (ocaml/merlin#1603)
    - Fix object method completion not working (ocaml/merlin#1606, fixes ocaml/merlin#1575)
    - Improve context detection for package types (ocaml/merlin#1608, fixes ocaml/merlin#1607)
    - Fix incorrect locations for string literals (ocaml/merlin#1574)
    - Fixed an issue that caused `errors` to erroneously alert about missing
      `cmi` files (ocaml/merlin#1577)
    - Prevent destruct from crashing on closed variant types (ocaml/merlin#1602,
      fixes ocaml/merlin#1601)
    - Improve longident parsing (ocaml/merlin#1612, fixes ocaml/merlin#945)
  + editor modes
    - emacs: call the user's configured completion UI in
      `merlin-construct` (ocaml/merlin#1598)
  + test suite
    - Add missing dependency to a test using ppxlib (ocaml/merlin#1583)
    - Add tests for the new PPX phase cache (ocaml/merlin#1584)
    - Add and update tests for `construct` ordering (ocaml/merlin#1603)
voodoos added a commit to voodoos/opam-repository that referenced this pull request May 26, 2023
CHANGES:

Fri May 26 15:23:42 CEST 2023

  + merlin binary
    - Allow monadic IO in dot protocol (ocaml/merlin#1581)
    - Add a `scope` option to the `occurrences` command in preparation for
      the upcoming `project-wide-occurrences` feature (ocaml/merlin#1596)
    - Construct bool-typed holes as `false` instead of `true` in the
      `construct` command, for consistency (ocaml/merlin#1599).
    - Add a hook to configure system command for spawning ppxes when Merlin is
      used as a library. (ocaml/merlin#1585)
    - Implement an all-or-nothing cache for the PPX phase (ocaml/merlin#1584)
    - Cleanup functors caches when backtracking, to avoid memory leaks
      (ocaml/merlin#1609, fixes ocaml/merlin#1529 and ocaml-lsp#1032)
    - Fix `construct` results ordering for sum types sand poly variants (ocaml/merlin#1603)
    - Fix object method completion not working (ocaml/merlin#1606, fixes ocaml/merlin#1575)
    - Improve context detection for package types (ocaml/merlin#1608, fixes ocaml/merlin#1607)
    - Fix incorrect locations for string literals (ocaml/merlin#1574)
    - Fixed an issue that caused `errors` to erroneously alert about missing
      `cmi` files (ocaml/merlin#1577)
    - Prevent destruct from crashing on closed variant types (ocaml/merlin#1602,
      fixes ocaml/merlin#1601)
    - Improve longident parsing (ocaml/merlin#1612, fixes ocaml/merlin#945)
  + editor modes
    - emacs: call the user's configured completion UI in
      `merlin-construct` (ocaml/merlin#1598)
  + test suite
    - Add missing dependency to a test using ppxlib (ocaml/merlin#1583)
    - Add tests for the new PPX phase cache (ocaml/merlin#1584)
    - Add and update tests for `construct` ordering (ocaml/merlin#1603)
voodoos added a commit to voodoos/opam-repository that referenced this pull request May 26, 2023
CHANGES:

Fri May 26 15:23:42 CEST 2023

  + merlin binary
    - Allow monadic IO in dot protocol (ocaml/merlin#1581)
    - Add a `scope` option to the `occurrences` command in preparation for
      the upcoming `project-wide-occurrences` feature (ocaml/merlin#1596)
    - Construct bool-typed holes as `false` instead of `true` in the
      `construct` command, for consistency (ocaml/merlin#1599).
    - Add a hook to configure system command for spawning ppxes when Merlin is
      used as a library. (ocaml/merlin#1585)
    - Implement an all-or-nothing cache for the PPX phase (ocaml/merlin#1584)
    - Cleanup functors caches when backtracking, to avoid memory leaks
      (ocaml/merlin#1609, fixes ocaml/merlin#1529 and ocaml-lsp#1032)
    - Fix `construct` results ordering for sum types sand poly variants (ocaml/merlin#1603)
    - Fix object method completion not working (ocaml/merlin#1606, fixes ocaml/merlin#1575)
    - Improve context detection for package types (ocaml/merlin#1608, fixes ocaml/merlin#1607)
    - Fix incorrect locations for string literals (ocaml/merlin#1574)
    - Fixed an issue that caused `errors` to erroneously alert about missing
      `cmi` files (ocaml/merlin#1577)
    - Prevent destruct from crashing on closed variant types (ocaml/merlin#1602,
      fixes ocaml/merlin#1601)
    - Improve longident parsing (ocaml/merlin#1612, fixes ocaml/merlin#945)
  + editor modes
    - emacs: call the user's configured completion UI in
      `merlin-construct` (ocaml/merlin#1598)
  + test suite
    - Add missing dependency to a test using ppxlib (ocaml/merlin#1583)
    - Add tests for the new PPX phase cache (ocaml/merlin#1584)
    - Add and update tests for `construct` ordering (ocaml/merlin#1603)

[new release] merlin, merlin-lib and dot-merlin-reader (4.9-414)

CHANGES:

Fri May 26 15:23:42 CEST 2023

  + merlin binary
    - Allow monadic IO in dot protocol (ocaml/merlin#1581)
    - Add a `scope` option to the `occurrences` command in preparation for
      the upcoming `project-wide-occurrences` feature (ocaml/merlin#1596)
    - Construct bool-typed holes as `false` instead of `true` in the
      `construct` command, for consistency (ocaml/merlin#1599).
    - Add a hook to configure system command for spawning ppxes when Merlin is
      used as a library. (ocaml/merlin#1585)
    - Implement an all-or-nothing cache for the PPX phase (ocaml/merlin#1584)
    - Cleanup functors caches when backtracking, to avoid memory leaks
      (ocaml/merlin#1609, fixes ocaml/merlin#1529 and ocaml-lsp#1032)
    - Fix `construct` results ordering for sum types sand poly variants (ocaml/merlin#1603)
    - Fix object method completion not working (ocaml/merlin#1606, fixes ocaml/merlin#1575)
    - Improve context detection for package types (ocaml/merlin#1608, fixes ocaml/merlin#1607)
    - Fix incorrect locations for string literals (ocaml/merlin#1574)
    - Fixed an issue that caused `errors` to erroneously alert about missing
      `cmi` files (ocaml/merlin#1577)
    - Prevent destruct from crashing on closed variant types (ocaml/merlin#1602,
      fixes ocaml/merlin#1601)
    - Improve longident parsing (ocaml/merlin#1612, fixes ocaml/merlin#945)
  + editor modes
    - emacs: call the user's configured completion UI in
      `merlin-construct` (ocaml/merlin#1598)
  + test suite
    - Add missing dependency to a test using ppxlib (ocaml/merlin#1583)
    - Add tests for the new PPX phase cache (ocaml/merlin#1584)
    - Add and update tests for `construct` ordering (ocaml/merlin#1603)
voodoos added a commit to voodoos/opam-repository that referenced this pull request May 31, 2023
CHANGES:

unreleased

  + merlin binary
    - Preview support for OCaml 5.1-alpha1. Short path is temporary disabled and
      inline records might not behave as expected.
    - Allow monadic IO in dot protocol (ocaml/merlin#1581)
    - Add a `scope` option to the `occurrences` command in preparation for
      the upcoming `project-wide-occurrences` feature (ocaml/merlin#1596)
    - Construct bool-typed holes as `false` instead of `true` in the
      `construct` command, for consistency (ocaml/merlin#1599).
    - Add a hook to configure system command for spawning ppxes when Merlin is
      used as a library. (ocaml/merlin#1585)
    - Implement an all-or-nothing cache for the PPX phase (ocaml/merlin#1584)
    - Cleanup functors caches when backtracking, to avoid memory leaks
      (ocaml/merlin#1609, fixes ocaml/merlin#1529 and ocaml-lsp#1032)
    - Fix `construct` results ordering for sum types sand poly variants (ocaml/merlin#1603)
    - Fix object method completion not working (ocaml/merlin#1606, fixes ocaml/merlin#1575)
    - Improve context detection for package types (ocaml/merlin#1608, fixes ocaml/merlin#1607)
    - Fix incorrect locations for string literals (ocaml/merlin#1574)
    - Fixed an issue that caused `errors` to erroneously alert about missing
      `cmi` files (ocaml/merlin#1577)
    - Prevent destruct from crashing on closed variant types (ocaml/merlin#1602,
      fixes ocaml/merlin#1601)
    - Improve longident parsing (ocaml/merlin#1612, fixes ocaml/merlin#945)
  + editor modes
    - emacs: call the user's configured completion UI in
      `merlin-construct` (ocaml/merlin#1598)
  + test suite
    - Add missing dependency to a test using ppxlib (ocaml/merlin#1583)
    - Add tests for the new PPX phase cache (ocaml/merlin#1584)
    - Add and update tests for `construct` ordering (ocaml/merlin#1603)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging this pull request may close these issues.

None yet

3 participants