From 526b9fbc6e53727245b9ea4ec3733cb4617f5114 Mon Sep 17 00:00:00 2001 From: Ulysse <5031221+voodoos@users.noreply.github.com> Date: Mon, 22 Feb 2021 11:42:57 +0100 Subject: [PATCH] [merlin] Handle filenames with dot-separated sections (#4264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Split filename on `.` and use the first part for config matching * Illustrate usage with some tests * Add some documentation about naming preprocessed files. Signed-off-by: Ulysse GĂ©rard --- doc/usage.rst | 108 +++++++++++++++++- src/dune_rules/merlin.ml | 9 +- .../test-cases/merlin/server.t/dune | 5 + .../merlin/server.t/not-a-module-name.ml | 1 + .../test-cases/merlin/server.t/run.t | 25 ++++ 5 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 test/blackbox-tests/test-cases/merlin/server.t/not-a-module-name.ml diff --git a/doc/usage.rst b/doc/usage.rst index 5f91fb92d04..83d00aebb73 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -280,8 +280,8 @@ There's a few aliases that dune automatically creates for the user defined in that directory. * ``check`` - This alias will build the minimal set of targets required for - tooling support. Essentially, this is ``.cmi``, ``.cmt``, ``.cmti``, and - ``.merlin`` files. + tooling support. Essentially, this is ``.cmi``, ``.cmt``, ``.cmti`` files and + Merlin configurations. Variables for artifacts ----------------------- @@ -542,3 +542,107 @@ The `--prefix` directory should be used to specify the destination. If you are using plugins that depends on installed libraries which are not dependencies of the executables -- so libraries that need to be loaded at runtime -- you must copy the libraries manually to the destination directory. + +Since version 2.8 Dune does not promote ``.merlin`` files to the source +directories any more. Instead these configurations are stored in the `_build` +folder and Merlin communicates directly with Dune to obtain its configuration +via the `ocaml-merlin` subcommand. The Merlin configuration is now stanza +specific allowing finer control. The following commands are not needed for +normal use of Dune and Merlin but can provide insightful informations when +debugging or configuring non-standard projects. + +Printing the configuration +-------------------------- + +It is possible to manually query the generated configuration for debugging +purposes: + +:: + + $ dune ocaml-merlin --dump-config + +This command will print the distinct configuration of each module present in the +current directory. This directory must be in a Dune workspace and the project +must be already built. The configuration will be encoded as a s-expressions, which +are used to communicate with Merlin. + +Printing an approximated ``.merlin`` +------------------------------------ + +It is also possible to print the configuration of the current folder in the +Merlin configuration syntax by running the following command: + +:: + + $ dune ocaml dump-dot-merlin > .merlin + +In that case only one configuration will be printed which is the result of a +coarse merge of the configurations of the various modules present in the current +folder. This folder must be in a Dune workspace and the project must be already +built. Preprocessing directives and other flags will be commented out and must +be un-commented afterward. This feature does not aim at writing exact or correct +``.merlin`` files, its sole purpose is to lessen the burden of writing the +configuration from scratch. + +Both these commands also support an optional path to specify the target +directory. This directory must be in a Dune workspace and the project must have +already been built. +======= + +Querying Merlin configuration +============================= + +Since version 2.8 Dune does not promote ``.merlin`` files to the source +directories any more. Instead these configurations are stored in the `_build` +folder and Merlin communicates directly with Dune to obtain its configuration +via the `ocaml-merlin` subcommand. The Merlin configuration is now stanza +specific allowing finer control. The following commands are not needed for +normal use of Dune and Merlin but can provide insightful informations when +debugging or configuring non-standard projects. + +Printing the configuration +-------------------------- + +It is possible to manually query the generated configuration for debugging +purposes: + +:: + + $ dune ocaml-merlin --dump-config + +This command will print the distinct configuration of each module present in the +current directory. This directory must be in a Dune workspace and the project +must be already built. The configuration will be encoded as a s-expressions, which +are used to communicate with Merlin. + +Printing an approximated ``.merlin`` +------------------------------------ + +It is also possible to print the configuration of the current folder in the +Merlin configuration syntax by running the following command: + +:: + + $ dune ocaml dump-dot-merlin > .merlin + +In that case only one configuration will be printed which is the result of a +coarse merge of the configurations of the various modules present in the current +folder. This folder must be in a Dune workspace and the project must be already +built. Preprocessing directives and other flags will be commented out and must +be un-commented afterward. This feature does not aim at writing exact or correct +``.merlin`` files, its sole purpose is to lessen the burden of writing the +configuration from scratch. + +Non-standard filenames +---------------------- + +Merlin configuration loading is based on filename. That means that if you have +files which are preprocessed by custom rules before they are built, they should +respect the following naming convention: the unprocessed file should start with +the name of the resulting processed file followed by a dot and then the rest +does not matter. Only the name before the first dot will be used by Dune to +match with available configurations. + +For example, if you use the ``cppo`` preprocessor to generate the file +``real_module_name.ml`` then the source file could be named +``real_module_name.cppo.ml``. diff --git a/src/dune_rules/merlin.ml b/src/dune_rules/merlin.ml index 1c377c020ed..e554887fbe8 100644 --- a/src/dune_rules/merlin.ml +++ b/src/dune_rules/merlin.ml @@ -79,7 +79,14 @@ module Processed = struct [ stdlib_dir; exclude_query_dir; obj_dirs; src_dirs; flags; suffixes ]) let get { modules; pp_config; config } ~filename = - let fname = Filename.remove_extension filename |> String.lowercase in + (* We only match the first part of the filename : foo.ml -> foo foo.cppo.ml + -> foo *) + let fname = + String.lsplit2 filename ~on:'.' + |> Option.map ~f:fst + |> Option.value ~default:filename + |> String.lowercase + in List.find_opt modules ~f:(fun name -> let fname' = Module_name.to_string name |> String.lowercase in String.equal fname fname') diff --git a/test/blackbox-tests/test-cases/merlin/server.t/dune b/test/blackbox-tests/test-cases/merlin/server.t/dune index 2867625c073..55775d0cd84 100644 --- a/test/blackbox-tests/test-cases/merlin/server.t/dune +++ b/test/blackbox-tests/test-cases/merlin/server.t/dune @@ -11,3 +11,8 @@ (name main) (modules main lib2) (libraries mylib mylib3)) + +(executable + (name not-a-module-name) + (modules not-a-module-name) + (flags :standard -w -24)) diff --git a/test/blackbox-tests/test-cases/merlin/server.t/not-a-module-name.ml b/test/blackbox-tests/test-cases/merlin/server.t/not-a-module-name.ml new file mode 100644 index 00000000000..bc3e757ef5d --- /dev/null +++ b/test/blackbox-tests/test-cases/merlin/server.t/not-a-module-name.ml @@ -0,0 +1 @@ +print_endline "I do not bear a valid module name." diff --git a/test/blackbox-tests/test-cases/merlin/server.t/run.t b/test/blackbox-tests/test-cases/merlin/server.t/run.t index 2286956fcca..aa92099ed2e 100644 --- a/test/blackbox-tests/test-cases/merlin/server.t/run.t +++ b/test/blackbox-tests/test-cases/merlin/server.t/run.t @@ -16,3 +16,28 @@ > (4:File${#FILE}:$FILE) > EOF ((?:STDLIB?:OPAM_PREFIX/lib/ocaml)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + +If a file has a name of the kind `module_name.xx.xxx.ml/i` +we consider it as ``module_name.ml/i` +This can be useful when some build scripts perform custom +preprocessing and copy files around. + $ FILE=lib3.foobar.ml + $ dune ocaml-merlin < (4:File${#FILE}:$FILE) + > EOF + ((?:STDLIB?:OPAM_PREFIX/lib/ocaml)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + +If a directory has no configuration the configuration of its parent is used +This can be useful when some build scripts copy files from subdirectories. + $ FILE=some_sub_dir/lib3.foobar.ml + $ dune ocaml-merlin < (4:File${#FILE}:$FILE) + > EOF + ((?:STDLIB?:OPAM_PREFIX/lib/ocaml)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.mylib.objs/byte)(?:B?:$TESTCASE_ROOT/_build/default/.mylib3.objs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-open?:Mylib?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs))) + +Test of an valid invalid module name + $ FILE=not-a-module-name.ml + $ dune ocaml-merlin < (4:File${#FILE}:$FILE) + > EOF + ((?:STDLIB?:OPAM_PREFIX/lib/ocaml)(?:EXCLUDE_QUERY_DIR)(?:B?:$TESTCASE_ROOT/_build/default/.not-a-module-name.eobjs/byte)(?:S?:$TESTCASE_ROOT)(?:FLG(?:-w?:@1..3@5..28@30..39@43@46..47@49..57@61..62-?:-strict-sequence?:-strict-formats?:-short-paths?:-keep-locs?:-w?:-24)))