Skip to content

Commit a31d48b

Browse files
committed
[display] import completion: suggest qualified paths for unqualified type prefixes
`import Fo|` used to fall into legacy path completion, which only lists root-package modules. Raise a toplevel CRImport collection instead, so clients can suggest `pack.to.Foo` (and sub-types, e.g. `Ba|` -> `pack.to.Foo.Bar`) with fully-qualified insert text. Qualified paths (`import pack.Fo|`) keep the package-scoped listing.
1 parent 6b2e796 commit a31d48b

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

src/context/display/displayPath.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ let handle_path_display ctx path p =
207207
with Not_found ->
208208
()
209209
end
210+
| (IDKModule([],s),p),DMDefault ->
211+
(* Unqualified module completion: offer the whole toplevel so that
212+
`import Fo|` can suggest `pack.to.Foo` (and its sub-types). *)
213+
DisplayToplevel.collect_and_raise ctx TKType WithType.no_value CRImport (s,p) p
210214
| (IDKModule(sl,s),p),_ ->
211215
raise (Parser.TypePath(sl,None,true,p))
212216
| (IDKSubType(sl,sm,st),p),(DMDefinition | DMTypeDefinition) ->

tests/server/src/cases/display/IImport.hx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,53 @@ class IImport extends DisplayTestCase {
6565
eq(true, hasPath(fields(1), "Context"));
6666
eq(false, hasPath(fields(1), "Context.hl"));
6767
}
68+
69+
/**
70+
import Fo{-1-};
71+
72+
class Main {}
73+
**/
74+
function testUnqualifiedModule(_) {
75+
vfs.putContent("pack/to/Foo.hx", "package pack.to;\nclass Foo {}\nclass Bar {}");
76+
var fields1 = fieldsWithClassPath(1);
77+
eq(true, hasFullPath(fields1, "pack.to", "Foo"));
78+
}
79+
80+
/**
81+
import Ba{-1-};
82+
83+
class Main {}
84+
**/
85+
function testUnqualifiedSubType(_) {
86+
vfs.putContent("pack/to/Foo.hx", "package pack.to;\nclass Foo {}\nclass Bar {}");
87+
var fields1 = fieldsWithClassPath(1);
88+
eq(true, hasFullPath(fields1, "pack.to", "Bar"));
89+
}
90+
91+
/**
92+
using Fo{-1-};
93+
94+
class Main {}
95+
**/
96+
function testUnqualifiedUsing(_) {
97+
vfs.putContent("pack/to/Foo.hx", "package pack.to;\nclass Foo {}\nclass Bar {}");
98+
var fields1 = fieldsWithClassPath(1);
99+
eq(true, hasFullPath(fields1, "pack.to", "Foo"));
100+
}
101+
102+
@:coroutine
103+
function fieldsWithClassPath(n:Int):Array<DisplayItem<Dynamic>> {
104+
// The implicit "" class path is excluded from toplevel exploration, so pass an explicit
105+
// one and force (re-)exploration — the context survives resetState between tests.
106+
runHaxeJson(["-cp", "."], ServerMethods.ReadClassPaths, {wait: true});
107+
var result = runHaxeJson(["-cp", "."], DisplayMethods.Completion, {file: file, offset: offset(n), wasAutoTriggered: false});
108+
return result.items;
109+
}
110+
111+
function hasFullPath<T>(items:Array<DisplayItem<T>>, pack:String, typeName:String):Bool {
112+
return items.exists(t -> switch (t.kind) {
113+
case Type: t.args.path.typeName == typeName && t.args.path.pack.join(".") == pack;
114+
case _: false;
115+
});
116+
}
68117
}

0 commit comments

Comments
 (0)