Skip to content

Commit 352fa02

Browse files
authored
Complete functions from included module (#7515)
* WIP find answer in stamps * Refactor included values * Fix check in processLocalValue * Clean up dump logs * Include values without pipe completion as well * Introduce include scope * Check if include ends with path * Deal with namespace in modulePathFromEnv * Simplify completions from current module * Also iter includes in findLocalCompletionsForValues * Add changelog entry * Remove unused function * Dump structure * Print scope * IncludePstr_include( Pmod_ident) * Remove path to_string * Don't let exported override included * Use Path.name * Remove duplicate check * Store included values in separate table
1 parent e63147f commit 352fa02

16 files changed

+618
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#### :house: Internal
4242

4343
- Refactor the ast for record expressions and patterns. https://github.com/rescript-lang/rescript/pull/7528
44+
- Editor: add completions from included modules. https://github.com/rescript-lang/rescript/pull/7515
4445

4546
# 12.0.0-alpha.13
4647

analysis/bin/main.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ let main () =
209209
| [_; "format"; path] ->
210210
Printf.printf "\"%s\"" (Json.escape (Commands.format ~path))
211211
| [_; "test"; path] -> Commands.test ~path
212+
| [_; "cmt"; rescript_json; cmt_path] -> CmtViewer.dump rescript_json cmt_path
212213
| args when List.mem "-h" args || List.mem "--help" args -> prerr_endline help
213214
| _ ->
214215
prerr_endline help;

analysis/src/CmtViewer.ml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
let filter_by_cursor cursor (loc : Warnings.loc) : bool =
2+
match cursor with
3+
| None -> true
4+
| Some (line, col) ->
5+
let start = loc.loc_start and end_ = loc.loc_end in
6+
let line_in = start.pos_lnum <= line && line <= end_.pos_lnum in
7+
let col_in =
8+
if start.pos_lnum = end_.pos_lnum then
9+
start.pos_cnum - start.pos_bol <= col
10+
&& col <= end_.pos_cnum - end_.pos_bol
11+
else if line = start.pos_lnum then col >= start.pos_cnum - start.pos_bol
12+
else if line = end_.pos_lnum then col <= end_.pos_cnum - end_.pos_bol
13+
else true
14+
in
15+
line_in && col_in
16+
17+
type filter = Cursor of (int * int) | Loc of Loc.t
18+
19+
let dump ?filter rescript_json cmt_path =
20+
let uri = Uri.fromPath (Filename.remove_extension cmt_path ^ ".res") in
21+
let package =
22+
let uri = Uri.fromPath rescript_json in
23+
Packages.getPackage ~uri |> Option.get
24+
in
25+
let moduleName =
26+
BuildSystem.namespacedName package.namespace (FindFiles.getName cmt_path)
27+
in
28+
match Cmt.fullForCmt ~moduleName ~package ~uri cmt_path with
29+
| None -> failwith (Format.sprintf "Could not load cmt for %s" cmt_path)
30+
| Some full ->
31+
let open SharedTypes in
32+
let open SharedTypes.Stamps in
33+
let applyFilter =
34+
match filter with
35+
| None -> fun _ -> true
36+
| Some (Cursor cursor) -> Loc.hasPos ~pos:cursor
37+
| Some (Loc loc) -> Loc.isInside loc
38+
in
39+
(match filter with
40+
| None -> ()
41+
| Some (Cursor (line, col)) ->
42+
Printf.printf "Filtering by cursor %d,%d\n" line col
43+
| Some (Loc loc) -> Printf.printf "Filtering by loc %s\n" (Loc.toString loc));
44+
45+
Printf.printf "file moduleName: %s\n\n" full.file.moduleName;
46+
47+
let stamps =
48+
full.file.stamps |> getEntries
49+
|> List.filter (fun (_, stamp) -> applyFilter (locOfKind stamp))
50+
in
51+
52+
let total_stamps = List.length stamps in
53+
Printf.printf "Found %d stamps:\n%s" total_stamps
54+
(if total_stamps > 0 then "\n" else "");
55+
56+
stamps
57+
|> List.sort (fun (_, a) (_, b) ->
58+
let aLoc = locOfKind a in
59+
let bLoc = locOfKind b in
60+
match compare aLoc.loc_start.pos_lnum bLoc.loc_start.pos_lnum with
61+
| 0 -> compare aLoc.loc_start.pos_cnum bLoc.loc_start.pos_cnum
62+
| c -> c)
63+
|> List.iter (fun (stamp, kind) ->
64+
match kind with
65+
| KType t ->
66+
Printf.printf "%d ktype %s\n" stamp
67+
(Warnings.loc_to_string t.extentLoc)
68+
| KValue t ->
69+
Printf.printf "%d kvalue %s\n" stamp
70+
(Warnings.loc_to_string t.extentLoc)
71+
| KModule t ->
72+
Printf.printf "%d kmodule %s\n" stamp
73+
(Warnings.loc_to_string t.extentLoc)
74+
| KConstructor t ->
75+
Printf.printf "%d kconstructor %s\n" stamp
76+
(Warnings.loc_to_string t.extentLoc));
77+
78+
(* dump the structure *)
79+
let rec dump_structure indent (structure : Module.structure) =
80+
if indent > 0 then Printf.printf "%s" (String.make indent ' ');
81+
Printf.printf "Structure %s:\n" structure.name;
82+
structure.items |> List.iter (dump_structure_item (indent + 2))
83+
and dump_structure_item indent item =
84+
if indent > 0 then Printf.printf "%s" (String.make indent ' ');
85+
let open Module in
86+
match item.kind with
87+
| Value _typedExpr ->
88+
Printf.printf "Value %s %s\n" item.name
89+
(Warnings.loc_to_string item.loc)
90+
| Type _ ->
91+
Printf.printf "Type %s %s\n" item.name (Warnings.loc_to_string item.loc)
92+
| Module {type_ = m} ->
93+
Printf.printf "Module %s %s\n" item.name
94+
(Warnings.loc_to_string item.loc);
95+
dump_module indent m
96+
and dump_module indent (module_ : Module.t) =
97+
match module_ with
98+
| Ident path -> Printf.printf "Module (Ident) %s\n" (Path.name path)
99+
| Structure structure -> dump_structure indent structure
100+
| Constraint (m1, m2) ->
101+
dump_module indent m1;
102+
dump_module indent m2
103+
in
104+
105+
print_newline ();
106+
dump_structure 0 full.file.structure;
107+
108+
(* Dump all locItems (typed nodes) *)
109+
let locItems =
110+
match full.extra with
111+
| {locItems} ->
112+
locItems |> List.filter (fun locItem -> applyFilter locItem.loc)
113+
in
114+
115+
Printf.printf "\nFound %d locItems (typed nodes):\n\n"
116+
(List.length locItems);
117+
118+
locItems
119+
|> List.sort (fun a b ->
120+
let aLoc = a.loc.Location.loc_start in
121+
let bLoc = b.loc.Location.loc_start in
122+
match compare aLoc.pos_lnum bLoc.pos_lnum with
123+
| 0 -> compare aLoc.pos_cnum bLoc.pos_cnum
124+
| c -> c)
125+
|> List.iter (fun {loc; locType} ->
126+
let locStr = Warnings.loc_to_string loc in
127+
let kindStr = SharedTypes.locTypeToString locType in
128+
Printf.printf "%s %s\n" locStr kindStr)

analysis/src/CompletionBackEnd.ml

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,30 @@ let processLocalModule name loc ~prefix ~exact ~env
451451
(Printf.sprintf "Completion Module Not Found %s loc:%s\n" name
452452
(Loc.toString loc))
453453

454+
let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t)
455+
~(localTables : LocalTables.t) =
456+
(* process only values for now *)
457+
localTables.includedValueTable
458+
|> Hashtbl.iter
459+
(fun (name, _) (declared : (string * Types.type_expr) Declared.t) ->
460+
(* We check all the values if their origin is the same as the include path. *)
461+
let source_module_path = fst declared.item in
462+
if String.ends_with ~suffix:includePath source_module_path then
463+
(* If this is the case we perform a similar check for the prefix *)
464+
if Utils.checkName name ~prefix ~exact then
465+
if not (Hashtbl.mem localTables.namesUsed name) then (
466+
Hashtbl.add localTables.namesUsed name ();
467+
localTables.resultRev <-
468+
{
469+
(Completion.create declared.name.txt ~env
470+
~kind:(Value (snd declared.item)))
471+
with
472+
deprecated = declared.deprecated;
473+
docstring = declared.docstring;
474+
synthetic = true;
475+
}
476+
:: localTables.resultRev))
477+
454478
let getItemsFromOpens ~opens ~localTables ~prefix ~exact ~completionContext =
455479
opens
456480
|> List.fold_left
@@ -465,8 +489,10 @@ let getItemsFromOpens ~opens ~localTables ~prefix ~exact ~completionContext =
465489
let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t)
466490
~env ~prefix ~exact ~opens ~scope =
467491
localTables |> LocalTables.populateValues ~env;
492+
localTables |> LocalTables.populateIncludedValues ~env;
468493
localTables |> LocalTables.populateConstructors ~env;
469494
localTables |> LocalTables.populateModules ~env;
495+
470496
scope
471497
|> Scope.iterValuesBeforeFirstOpen
472498
(processLocalValue ~prefix ~exact ~env ~localTables);
@@ -491,11 +517,16 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t)
491517
scope
492518
|> Scope.iterModulesAfterFirstOpen
493519
(processLocalModule ~prefix ~exact ~env ~localTables);
520+
521+
scope
522+
|> Scope.iterIncludes (processLocalInclude ~prefix ~exact ~env ~localTables);
523+
494524
List.rev_append localTables.resultRev valuesFromOpens
495525

496526
let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix
497527
~exact ~opens ~scope =
498528
localTables |> LocalTables.populateValues ~env;
529+
localTables |> LocalTables.populateIncludedValues ~env;
499530
localTables |> LocalTables.populateModules ~env;
500531
scope
501532
|> Scope.iterValuesBeforeFirstOpen
@@ -515,6 +546,10 @@ let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix
515546
scope
516547
|> Scope.iterModulesAfterFirstOpen
517548
(processLocalModule ~prefix ~exact ~env ~localTables);
549+
550+
scope
551+
|> Scope.iterIncludes (processLocalInclude ~prefix ~exact ~env ~localTables);
552+
518553
List.rev_append localTables.resultRev valuesFromOpens
519554

520555
let findLocalCompletionsForTypes ~(localTables : LocalTables.t) ~env ~prefix
@@ -1049,6 +1084,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10491084
| None -> [])
10501085
| CPPipe {contextPath = cp; id = prefix; lhsLoc; inJsx; synthetic} -> (
10511086
if Debug.verbose () then print_endline "[ctx_path]--> CPPipe";
1087+
(* The environment at the cursor is the environment we're completing from. *)
1088+
let env_at_cursor = env in
10521089
match
10531090
cp
10541091
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
@@ -1175,8 +1212,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
11751212
in
11761213
(* Add completions from the current module. *)
11771214
let currentModuleCompletions =
1178-
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom
1179-
~opens:[] ~pos ~scope ~debug ~prefix ~env ~rawOpens ~full []
1215+
getCompletionsForPath ~debug ~completionContext:Value ~exact:false
1216+
~opens:[] ~full ~pos ~env:env_at_cursor ~scope [prefix]
11801217
|> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full
11811218
~targetTypeId:mainTypeId
11821219
in

analysis/src/CompletionFrontEnd.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
743743
mbs |> List.iter scopeModuleBinding;
744744
mbs |> List.iter (fun b -> iterator.module_binding iterator b);
745745
processed := true
746+
| Pstr_include {pincl_mod = {pmod_desc = med}} -> (
747+
match med with
748+
| Pmod_ident {txt = lid; loc}
749+
| Pmod_apply ({pmod_desc = Pmod_ident {txt = lid; loc}}, _) ->
750+
let module_name = Longident.flatten lid |> String.concat "." in
751+
scope := !scope |> Scope.addInclude ~name:module_name ~loc
752+
| _ -> ())
746753
| _ -> ());
747754
if not !processed then
748755
Ast_iterator.default_iterator.structure_item iterator item

analysis/src/Completions.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ let getCompletions ~debug ~path ~pos ~currentFile ~forHover =
99
with
1010
| None -> None
1111
| Some (completable, scope) -> (
12+
(* uncomment when debugging *)
13+
if false then (
14+
Printf.printf "\nScope from frontend:\n";
15+
List.iter
16+
(fun item ->
17+
Printf.printf "%s\n" (SharedTypes.ScopeTypes.item_to_string item))
18+
scope;
19+
print_newline ());
1220
(* Only perform expensive ast operations if there are completables *)
1321
match Cmt.loadFullCmtFromPath ~path with
1422
| None -> None

analysis/src/Loc.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,9 @@ let rangeOfLoc (loc : t) =
2121
let start = loc |> start |> mkPosition in
2222
let end_ = loc |> end_ |> mkPosition in
2323
{Protocol.start; end_}
24+
25+
let isInside (x : t) (y : t) =
26+
x.loc_start.pos_cnum >= y.loc_start.pos_cnum
27+
&& x.loc_end.pos_cnum <= y.loc_end.pos_cnum
28+
&& x.loc_start.pos_lnum >= y.loc_start.pos_lnum
29+
&& x.loc_end.pos_lnum <= y.loc_end.pos_lnum

analysis/src/LocalTables.ml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type t = {
1010
modulesTable: Module.t table;
1111
typesTable: Type.t table;
1212
valueTable: Types.type_expr table;
13+
includedValueTable: (string * Types.type_expr) table;
1314
}
1415

1516
let create () =
@@ -20,6 +21,7 @@ let create () =
2021
modulesTable = Hashtbl.create 1;
2122
typesTable = Hashtbl.create 1;
2223
valueTable = Hashtbl.create 1;
24+
includedValueTable = Hashtbl.create 1;
2325
}
2426

2527
let populateValues ~env localTables =
@@ -29,6 +31,18 @@ let populateValues ~env localTables =
2931
(declared.name.txt, declared.name.loc |> Loc.start)
3032
declared)
3133

34+
let populateIncludedValues ~env localTables =
35+
env.QueryEnv.file.stamps
36+
|> Stamps.iterValues (fun _ declared ->
37+
match declared.modulePath with
38+
| ModulePath.IncludedModule (source, _) ->
39+
let path = Path.name source in
40+
let declared = {declared with item = (path, declared.item)} in
41+
Hashtbl.replace localTables.includedValueTable
42+
(declared.name.txt, declared.name.loc |> Loc.start)
43+
declared
44+
| _ -> ())
45+
3246
let populateConstructors ~env localTables =
3347
env.QueryEnv.file.stamps
3448
|> Stamps.iterConstructors (fun _ declared ->

analysis/src/Scope.ml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ let itemToString item =
1414
| Module (s, loc) -> "Module " ^ s ^ " " ^ Loc.toString loc
1515
| Value (s, loc, _, _) -> "Value " ^ s ^ " " ^ Loc.toString loc
1616
| Type (s, loc) -> "Type " ^ s ^ " " ^ Loc.toString loc
17+
| Include (s, loc) -> "Include " ^ s ^ " " ^ Loc.toString loc
1718
[@@live]
1819

1920
let create () : t = []
@@ -32,6 +33,7 @@ let addValue ~name ~loc ?contextPath x =
3233
(SharedTypes.Completable.contextPathToString contextPath));
3334
Value (name, loc, contextPath, x) :: x
3435
let addType ~name ~loc x = Type (name, loc) :: x
36+
let addInclude ~name ~loc x = Include (name, loc) :: x
3537

3638
let iterValuesBeforeFirstOpen f x =
3739
let rec loop items =
@@ -129,6 +131,17 @@ let iterModulesAfterFirstOpen f x =
129131
in
130132
loop false x
131133

134+
let iterIncludes f x =
135+
let rec loop items =
136+
match items with
137+
| [] -> ()
138+
| Include (s, loc) :: rest ->
139+
f s loc;
140+
loop rest
141+
| _ :: rest -> loop rest
142+
in
143+
loop x
144+
132145
let getRawOpens x =
133146
x
134147
|> Utils.filterMap (function

0 commit comments

Comments
 (0)