Skip to content

Commit 3f05a86

Browse files
committed
fix: merlin_config when nested dune-{project,workspace} files exist
1 parent c22fb90 commit 3f05a86

File tree

9 files changed

+78
-27
lines changed

9 files changed

+78
-27
lines changed

ocaml-lsp-server/src/merlin_config.ml

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -255,38 +255,45 @@ let find_project_context start_dir =
255255
need to keep track of this folder because [dune ocaml-merlin] might be
256256
started from a folder that is a parent of the [workdir]. Thus we cannot
257257
always use that starting folder as the workdir. *)
258-
let map_workdir dir = function
259-
| Some dir -> Some dir
260-
| None ->
261-
(* XXX what's ["dune-file"]? *)
262-
let fnames = List.map ~f:(Filename.concat dir) [ "dune"; "dune-file" ] in
263-
if List.exists ~f:file_exists fnames then
264-
Some dir
265-
else
266-
None
258+
let rec outermost ~dir search_files =
259+
let cur_match =
260+
search_files
261+
|> List.find_map ~f:(fun f ->
262+
let fname = Filename.concat dir f in
263+
if file_exists fname then
264+
Some (dir, fname)
265+
else
266+
None)
267+
in
268+
let parent = Filename.dirname dir in
269+
if parent = dir then
270+
cur_match
271+
else
272+
match outermost ~dir:parent search_files with
273+
| Some outer -> Some outer
274+
| None -> cur_match
267275
in
268276

269-
let rec loop workdir dir =
270-
match
271-
List.find_map [ "dune-project"; "dune-workspace" ] ~f:(fun f ->
272-
let fname = Filename.concat dir f in
273-
if file_exists fname then
274-
let workdir = Option.value ~default:dir workdir in
275-
Some ({ workdir; process_dir = dir }, fname)
276-
else
277-
None)
278-
with
279-
| Some s -> Some s
277+
let rec innermost ~dir search_files =
278+
let cur_match =
279+
search_files
280+
|> List.find_opt ~f:(fun f -> file_exists (Filename.concat dir f))
281+
in
282+
match cur_match with
283+
| Some dir -> Some dir
280284
| None ->
281285
let parent = Filename.dirname dir in
282-
if parent <> dir then
283-
(* Was this directory the workdir ? *)
284-
let workdir = map_workdir dir workdir in
285-
loop workdir parent
286-
else
286+
if parent = dir then
287287
None
288+
else
289+
innermost ~dir:parent search_files
288290
in
289-
loop None start_dir
291+
let workdir = innermost ~dir:start_dir [ "dune" ] in
292+
match outermost ~dir:start_dir [ "dune-workspace"; "dune-project" ] with
293+
| Some (dir, f) ->
294+
let workdir = workdir |> Option.value ~default:dir in
295+
Some ({ workdir; process_dir = dir }, f)
296+
| _ -> None
290297

291298
let get_external_config db (t : Mconfig.t) path =
292299
let path = Misc.canonicalize_filename path in
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
(library
2-
(name declaration_files))
2+
(name declaration_files)
3+
(libraries inner))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(library
2+
(name inner))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(lang dune 2.5)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let x = 2
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
val x : int
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let y = Inner_lib.x
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
let y = Lib.x
2+
3+
let z = Inner.Inner_lib.x

ocaml-lsp-server/test/e2e/__tests__/textDocument-declaration.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,21 @@ describe("textDocument/declaration", () => {
1010

1111
let testWorkspacePath = path.join(__dirname, "declaration_files/");
1212

13+
let outerDuneProjectPath = path.join(__dirname, "../../../../dune-project");
14+
15+
1316
let createPathForFile = (filename: string) =>
1417
path.join(testWorkspacePath, filename);
1518

1619
beforeEach(async () => {
20+
await fs.rename(outerDuneProjectPath, outerDuneProjectPath + ".bak");
1721
languageServer = await LanguageServer.startAndInitialize();
1822
});
1923

2024
afterEach(async () => {
2125
await LanguageServer.exit(languageServer);
2226
languageServer = null;
27+
await fs.rename(outerDuneProjectPath + ".bak", outerDuneProjectPath);
2328
});
2429

2530
async function openDocument(filepath) {
@@ -57,5 +62,35 @@ describe("textDocument/declaration", () => {
5762
start: { character: 0, line: 0 },
5863
});
5964
expect(result[0].uri).toEqualUri(testUri(createPathForFile("lib.mli")));
65+
66+
let resultFromInner = await queryDeclaration(
67+
createPathForFile("main.ml"),
68+
Types.Position.create(2, 25),
69+
);
70+
71+
expect(resultFromInner.length).toBe(1);
72+
expect(resultFromInner[0].range).toMatchObject({
73+
end: { character: 0, line: 0 },
74+
start: { character: 0, line: 0 },
75+
});
76+
expect(resultFromInner[0].uri).toEqualUri(testUri(createPathForFile("inner/inner_lib.mli")));
77+
});
78+
79+
it("returns location of a declaration in an inner project", async () => {
80+
child_process.execSync("dune build", { cwd: testWorkspacePath });
81+
82+
await openDocument(createPathForFile("inner/inner_main.ml"));
83+
84+
let result = await queryDeclaration(
85+
createPathForFile("inner/inner_main.ml"),
86+
Types.Position.create(0, 13),
87+
);
88+
89+
expect(result.length).toBe(1);
90+
expect(result[0].range).toMatchObject({
91+
end: { character: 0, line: 0 },
92+
start: { character: 0, line: 0 },
93+
});
94+
expect(result[0].uri).toEqualUri(testUri(createPathForFile("inner/inner_lib.mli")));
6095
});
6196
});

0 commit comments

Comments
 (0)