From 182792005df5aefff0f3de2f9ebdd7bfc09b25c2 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 23 May 2025 23:22:14 +0200 Subject: [PATCH 01/20] WIP find answer in stamps --- analysis/bin/main.ml | 1 + analysis/src/CmtViewer.ml | 101 ++++++++++++++ analysis/src/CompletionBackEnd.ml | 126 +++++++++++++----- analysis/src/Loc.ml | 6 + analysis/src/SharedTypes.ml | 27 ++++ analysis/src/TypeUtils.ml | 19 +++ .../src/DotPipeCompleteFromCurrentModule.res | 63 ++++++++- 7 files changed, 312 insertions(+), 31 deletions(-) create mode 100644 analysis/src/CmtViewer.ml diff --git a/analysis/bin/main.ml b/analysis/bin/main.ml index 259b1a3004..f25cb78115 100644 --- a/analysis/bin/main.ml +++ b/analysis/bin/main.ml @@ -209,6 +209,7 @@ let main () = | [_; "format"; path] -> Printf.printf "\"%s\"" (Json.escape (Commands.format ~path)) | [_; "test"; path] -> Commands.test ~path + | [_; "cmt"; rescript_json; cmt_path] -> CmtViewer.dump rescript_json cmt_path | args when List.mem "-h" args || List.mem "--help" args -> prerr_endline help | _ -> prerr_endline help; diff --git a/analysis/src/CmtViewer.ml b/analysis/src/CmtViewer.ml new file mode 100644 index 0000000000..b41d97ef72 --- /dev/null +++ b/analysis/src/CmtViewer.ml @@ -0,0 +1,101 @@ +let loc_to_string (loc : Warnings.loc) : string = + Format.sprintf "(%03d,%03d--%03d,%03d)" loc.loc_start.pos_lnum + (loc.loc_start.pos_cnum - loc.loc_start.pos_bol) + loc.loc_end.pos_lnum + (loc.loc_end.pos_cnum - loc.loc_end.pos_bol) + +let filter_by_cursor cursor (loc : Warnings.loc) : bool = + match cursor with + | None -> true + | Some (line, col) -> + let start = loc.loc_start and end_ = loc.loc_end in + let line_in = start.pos_lnum <= line && line <= end_.pos_lnum in + let col_in = + if start.pos_lnum = end_.pos_lnum then + start.pos_cnum - start.pos_bol <= col + && col <= end_.pos_cnum - end_.pos_bol + else if line = start.pos_lnum then col >= start.pos_cnum - start.pos_bol + else if line = end_.pos_lnum then col <= end_.pos_cnum - end_.pos_bol + else true + in + line_in && col_in + +type filter = Cursor of (int * int) | Loc of Loc.t + +let dump ?filter rescript_json cmt_path = + let uri = Uri.fromPath (Filename.remove_extension cmt_path ^ ".res") in + let package = + let uri = Uri.fromPath rescript_json in + Packages.getPackage ~uri |> Option.get + in + let moduleName = + BuildSystem.namespacedName package.namespace (FindFiles.getName cmt_path) + in + match Cmt.fullForCmt ~moduleName ~package ~uri cmt_path with + | None -> failwith (Format.sprintf "Could not load cmt for %s" cmt_path) + | Some full -> + let open SharedTypes in + let open SharedTypes.Stamps in + let applyFilter = + match filter with + | None -> fun _ -> true + | Some (Cursor cursor) -> Loc.hasPos ~pos:cursor + | Some (Loc loc) -> Loc.isInside loc + in + (match filter with + | None -> () + | Some (Cursor (line, col)) -> + Printf.printf "Filtering by cursor %d,%d\n" line col + | Some (Loc loc) -> Printf.printf "Filtering by loc %s\n" (Loc.toString loc)); + let stamps = + full.file.stamps |> getEntries + |> List.filter (fun (_, stamp) -> applyFilter (locOfKind stamp)) + in + + let total_stamps = List.length stamps in + Printf.printf "Found %d stamps:\n%s" total_stamps + (if total_stamps > 0 then "\n" else ""); + + stamps + |> List.sort (fun (_, a) (_, b) -> + let aLoc = locOfKind a in + let bLoc = locOfKind b in + match compare aLoc.loc_start.pos_lnum bLoc.loc_start.pos_lnum with + | 0 -> compare aLoc.loc_start.pos_cnum bLoc.loc_start.pos_cnum + | c -> c) + |> List.iter (fun (stamp, kind) -> + match kind with + | KType t -> + Printf.printf "%d ktype %s\n" stamp + (loc_to_string t.extentLoc) + | KValue t -> + Printf.printf "%d kvalue %s\n" stamp + (loc_to_string t.extentLoc) + | KModule t -> + Printf.printf "%d kmodule %s\n" stamp + (loc_to_string t.extentLoc) + | KConstructor t -> + Printf.printf "%d kconstructor %s\n" stamp + (loc_to_string t.extentLoc)); + + (* Dump all locItems (typed nodes) *) + let locItems = + match full.extra with + | {locItems} -> + locItems |> List.filter (fun locItem -> applyFilter locItem.loc) + in + + Printf.printf "\nFound %d locItems (typed nodes):\n\n" + (List.length locItems); + + locItems + |> List.sort (fun a b -> + let aLoc = a.loc.Location.loc_start in + let bLoc = b.loc.Location.loc_start in + match compare aLoc.pos_lnum bLoc.pos_lnum with + | 0 -> compare aLoc.pos_cnum bLoc.pos_cnum + | c -> c) + |> List.iter (fun {loc; locType} -> + let locStr = loc_to_string loc in + let kindStr = SharedTypes.locTypeToString locType in + Printf.printf "%s %s\n" locStr kindStr) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 96dab722d1..efde15a400 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -96,6 +96,13 @@ let completionForExportedModules ~env ~prefix ~exact ~namesUsed = {docstring = declared.docstring; module_ = declared.item}) let completionForExportedValues ~env ~prefix ~exact ~namesUsed = + (* dump the contents of env.QueryEnv.file.stamps *) + Stamps.iterValues + (fun stamp declared -> + Format.printf "stamp: %d name: %s loc: %s\n" stamp declared.name.txt + (Loc.toString declared.extentLoc)) + env.QueryEnv.file.stamps; + completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Value) (Stamps.findValue env.file.stamps) ~prefix ~exact ~env ~namesUsed (fun declared -> Completion.Value declared.item) @@ -353,33 +360,32 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed let processLocalValue name loc contextPath scope ~prefix ~exact ~env ~(localTables : LocalTables.t) = if Utils.checkName name ~prefix ~exact then - match Hashtbl.find_opt localTables.valueTable (name, Loc.start loc) with - | Some declared -> - if not (Hashtbl.mem localTables.namesUsed name) then ( - Hashtbl.add localTables.namesUsed name (); - localTables.resultRev <- - { - (Completion.create declared.name.txt ~env ~kind:(Value declared.item)) - with - deprecated = declared.deprecated; - docstring = declared.docstring; - } - :: localTables.resultRev) - | None -> - if !Cfg.debugFollowCtxPath then - Printf.printf "Completion Value Not Found %s loc:%s\n" name - (Loc.toString loc); + print_endline + ("processLocalValue name: " ^ name ^ " loc: " ^ Loc.toString loc); + match Hashtbl.find_opt localTables.valueTable (name, Loc.start loc) with + | Some declared -> + if not (Hashtbl.mem localTables.namesUsed name) then ( + Hashtbl.add localTables.namesUsed name (); localTables.resultRev <- - Completion.create name ~env - ~kind: - (match contextPath with - | Some contextPath -> FollowContextPath (contextPath, scope) - | None -> - Value - (Ctype.newconstr - (Path.Pident (Ident.create "Type Not Known")) - [])) - :: localTables.resultRev + { + (Completion.create declared.name.txt ~env ~kind:(Value declared.item)) with + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: localTables.resultRev) + | None -> + if !Cfg.debugFollowCtxPath then + Printf.printf "Completion Value Not Found %s loc:%s\n" name + (Loc.toString loc); + localTables.resultRev <- + Completion.create name ~env + ~kind: + (match contextPath with + | Some contextPath -> FollowContextPath (contextPath, scope) + | None -> + Value + (Ctype.newconstr (Path.Pident (Ident.create "Type Not Known")) [])) + :: localTables.resultRev let processLocalConstructor name loc ~prefix ~exact ~env ~(localTables : LocalTables.t) = @@ -467,6 +473,21 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) localTables |> LocalTables.populateValues ~env; localTables |> LocalTables.populateConstructors ~env; localTables |> LocalTables.populateModules ~env; + + (* dump the contents of localTables.valueTable *) + Hashtbl.iter + (fun (name, (l, c)) declared -> + Format.printf "name: %s loc: %d %d type: %s\n" name l c + (Shared.typeToString declared.Declared.item)) + localTables.valueTable; + + scope + |> List.iter (fun scope_item -> + match scope_item with + | ScopeTypes.Value (name, _, _, _) -> + Format.printf "scope_item: %s\n" name + | _ -> ()); + scope |> Scope.iterValuesBeforeFirstOpen (processLocalValue ~prefix ~exact ~env ~localTables); @@ -491,6 +512,13 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) scope |> Scope.iterModulesAfterFirstOpen (processLocalModule ~prefix ~exact ~env ~localTables); + + (* dump the contents of localTables.valueTable *) + List.iter + (fun completion -> + Format.printf "resultRev name: %s\n" completion.Completion.name) + localTables.resultRev; + List.rev_append localTables.resultRev valuesFromOpens let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix @@ -560,12 +588,13 @@ let findLocalCompletionsForModules ~(localTables : LocalTables.t) ~env ~prefix let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~scope ~(completionContext : Completable.completionContext) = (* TODO: handle arbitrary interleaving of opens and local bindings correctly *) - Log.log + print_endline ("findLocalCompletionsWithOpens uri:" ^ Uri.toString env.file.uri ^ " pos:" ^ Pos.toString pos); let localTables = LocalTables.create () in match completionContext with | Value | ValueOrField -> + print_endline "Value or Field"; findLocalCompletionsForValuesAndConstructors ~localTables ~env ~prefix ~exact ~opens ~scope | Type -> @@ -611,6 +640,16 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~scope ~completionContext in + print_endline + ("localCompletionsWithOpens: " + ^ (List.map + (fun c -> + match c.Completion.sortText with + | None -> c.Completion.name + | Some insertText -> c.Completion.name ^ " " ^ insertText) + localCompletionsWithOpens + |> String.concat ",")); + let fileModules = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> @@ -622,7 +661,9 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope then Some (Completion.create name ~env ~kind:(Completion.FileModule name)) - else None) + else ( + print_endline ("filterMap name: " ^ name); + None)) in localCompletionsWithOpens @ fileModules | moduleName :: path -> ( @@ -1173,10 +1214,35 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact ~full ~rawOpens typ else [] in + let ownModule = TypeUtils.find_module_path_at_pos ~full ~pos in + Format.printf "ownModule: %s\n" (String.concat "." ownModule); (* Add completions from the current module. *) + let allModuleValueCompletions ~env = + let completions = ref [] in + Stamps.iterValues + (fun _stamp declared -> + match declared.modulePath with + | SharedTypes.ModulePath.IncludedModule (_path, ownerPath) -> + let ownerPath = + ModulePath.toPathWithPrefix ownerPath + full.file.File.moduleName + in + Format.printf "ownerPath: %s \n" (ownerPath |> String.concat "."); + if ownerPath = ownModule then + completions := + Completion.create declared.name.txt ~env + ~kind:(Value declared.item) + :: !completions + | _ -> ()) + env.file.stamps; + !completions + in let currentModuleCompletions = - completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom - ~opens:[] ~pos ~scope ~debug ~prefix ~env ~rawOpens ~full [] + allModuleValueCompletions ~env |> fun tap -> + print_endline + ("Unfiltered pipe completions: " + ^ String.concat ", " (List.map (fun c -> c.Completion.name) tap)); + tap |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full ~targetTypeId:mainTypeId in diff --git a/analysis/src/Loc.ml b/analysis/src/Loc.ml index 2ab1d8fbd2..635b787128 100644 --- a/analysis/src/Loc.ml +++ b/analysis/src/Loc.ml @@ -21,3 +21,9 @@ let rangeOfLoc (loc : t) = let start = loc |> start |> mkPosition in let end_ = loc |> end_ |> mkPosition in {Protocol.start; end_} + +let isInside (x : t) (y : t) = + x.loc_start.pos_cnum >= y.loc_start.pos_cnum + && x.loc_end.pos_cnum <= y.loc_end.pos_cnum + && x.loc_start.pos_lnum >= y.loc_start.pos_lnum + && x.loc_end.pos_lnum <= y.loc_end.pos_lnum diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 13692e26a6..fb7be4e58c 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -24,6 +24,16 @@ module ModulePath = struct | NotVisible -> current in loop modulePath [tipName] + + let toPathWithPrefix modulePath prefix : path = + let rec loop modulePath current = + match modulePath with + | File _ -> current + | IncludedModule (_, inner) -> loop inner current + | ExportedModule {name; modulePath = inner} -> loop inner (name :: current) + | NotVisible -> current + in + prefix :: loop modulePath [] end type field = { @@ -155,6 +165,14 @@ module Declared = struct end module Stamps : sig + type kind = + | KType of Type.t Declared.t + | KValue of Types.type_expr Declared.t + | KModule of Module.t Declared.t + | KConstructor of Constructor.t Declared.t + + val locOfKind : kind -> Warnings.loc + type t val addConstructor : t -> int -> Constructor.t Declared.t -> unit @@ -169,6 +187,7 @@ module Stamps : sig val iterModules : (int -> Module.t Declared.t -> unit) -> t -> unit val iterTypes : (int -> Type.t Declared.t -> unit) -> t -> unit val iterValues : (int -> Types.type_expr Declared.t -> unit) -> t -> unit + val getEntries : t -> (int * kind) list end = struct type 't stampMap = (int, 't Declared.t) Hashtbl.t @@ -178,6 +197,12 @@ end = struct | KModule of Module.t Declared.t | KConstructor of Constructor.t Declared.t + let locOfKind = function + | KType declared -> declared.extentLoc + | KValue declared -> declared.extentLoc + | KModule declared -> declared.extentLoc + | KConstructor declared -> declared.extentLoc + type t = (int, kind) Hashtbl.t let init () = Hashtbl.create 10 @@ -239,6 +264,8 @@ end = struct | KConstructor d -> f stamp d | _ -> ()) stamps + + let getEntries t = t |> Hashtbl.to_seq |> List.of_seq end module File = struct diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 4c89a2c117..f1547b4a82 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1271,3 +1271,22 @@ let completionPathFromMaybeBuiltin path = (* Route Stdlib_X to Stdlib.X for proper completions without the Stdlib_ prefix *) Some (String.split_on_char '_' mainModule) | _ -> None) + +let find_module_path_at_pos ~(full : SharedTypes.full) ~(pos : int * int) = + let rec aux (structure : Module.structure) (path_acc : string list) = + let found = + structure.Module.items + |> List.find_map (fun item -> + match item.Module.kind with + | SharedTypes.Module.Module {type_ = Structure substructure; _} -> + let loc = item.loc in + if CursorPosition.locHasCursor loc ~pos then + Some (aux substructure (path_acc @ [item.name])) + else None + | _ -> None) + in + match found with + | Some path -> path + | None -> path_acc + in + aux full.file.File.structure [full.file.File.moduleName] diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index 819086e35a..09762851fc 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -9,9 +9,70 @@ module Y = { let a = (x:t) => { // x. - // ^com + // com () } let b = (x:t) => 4 +} + + +module Types = { + type comp + type context + type vec2 +} + +module PosComp = ( + T: { + type t + }, +) => { + open Types + + @send + external addPos: (context, float, float) => comp = "pos" + + @send + external addPosFromVec2: (context, vec2) => comp = "pos" +} + +module SpriteComp = ( + T: { + type t + } +) + => { + open Types + + @send + external addSprite: (context, string) => comp = "sprite" + } + +external k: Types.context = "k" + +module Wall = { + type t + + include PosComp({ type t = t }) + + let make = () => { + [ + // k. + // ^com + ] + } + + module Poster = { + type t + + include SpriteComp({ type t = t }) + + let make = () => { + [ + // k. + // com + ] + } + } } \ No newline at end of file From a9e37c0377f3f218fa17b9ff5aaad230a651452d Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 24 May 2025 10:12:42 +0200 Subject: [PATCH 02/20] Refactor included values --- analysis/src/CompletionBackEnd.ml | 70 ++++++++++--------- .../src/DotPipeCompleteFromCurrentModule.res | 4 +- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index efde15a400..b39f0997d6 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -661,9 +661,7 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope then Some (Completion.create name ~env ~kind:(Completion.FileModule name)) - else ( - print_endline ("filterMap name: " ^ name); - None)) + else None) in localCompletionsWithOpens @ fileModules | moduleName :: path -> ( @@ -814,6 +812,33 @@ let completionsGetCompletionType ~full completions = | Some {Completion.kind = ExtractedType (typ, _); env} -> Some (typ, env) | _ -> None +(** + Returns completions from the current module where `include OtherModule` is present. +*) +let completionsFromIncludedModule ~full ~env ~pos = + (* Get the path of the module where the cursor is.*) + let ownModule = TypeUtils.find_module_path_at_pos ~full ~pos in + + env.file.stamps |> Stamps.getEntries + |> List.filter_map (fun (_stamp, kind) -> + match kind with + | SharedTypes.Stamps.KValue + { + modulePath = + SharedTypes.ModulePath.IncludedModule (_path, ownerPath); + name; + item; + } -> + let ownerPath = + ModulePath.toPathWithPrefix ownerPath full.file.File.moduleName + in + Format.printf "ownerPath: %s \n" (ownerPath |> String.concat "."); + if ownerPath = ownModule then ( + print_endline "hit"; + Some (Completion.create name.txt ~env ~kind:(Value item))) + else None + | _ -> None) + let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos completions = let firstNonSyntheticCompletion = @@ -1214,40 +1239,21 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact ~full ~rawOpens typ else [] in - let ownModule = TypeUtils.find_module_path_at_pos ~full ~pos in - Format.printf "ownModule: %s\n" (String.concat "." ownModule); - (* Add completions from the current module. *) - let allModuleValueCompletions ~env = - let completions = ref [] in - Stamps.iterValues - (fun _stamp declared -> - match declared.modulePath with - | SharedTypes.ModulePath.IncludedModule (_path, ownerPath) -> - let ownerPath = - ModulePath.toPathWithPrefix ownerPath - full.file.File.moduleName - in - Format.printf "ownerPath: %s \n" (ownerPath |> String.concat "."); - if ownerPath = ownModule then - completions := - Completion.create declared.name.txt ~env - ~kind:(Value declared.item) - :: !completions - | _ -> ()) - env.file.stamps; - !completions - in let currentModuleCompletions = - allModuleValueCompletions ~env |> fun tap -> - print_endline - ("Unfiltered pipe completions: " - ^ String.concat ", " (List.map (fun c -> c.Completion.name) tap)); - tap + completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom + ~opens:[] ~pos ~scope ~debug ~prefix ~env ~rawOpens ~full [] |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full ~targetTypeId:mainTypeId in + let ownIncludedModuleCompletions = + [] + (* completionsFromIncludedModule ~full ~env ~pos + |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full + ~targetTypeId:mainTypeId *) + in jsxCompletions @ pipeCompletions @ extraCompletions - @ currentModuleCompletions @ globallyConfiguredCompletions)) + @ currentModuleCompletions @ ownIncludedModuleCompletions + @ globallyConfiguredCompletions)) | CTuple ctxPaths -> if Debug.verbose () then print_endline "[ctx_path]--> CTuple"; (* Turn a list of context paths into a list of type expressions. *) diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index 09762851fc..7addcf981a 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -59,7 +59,7 @@ module Wall = { let make = () => { [ // k. - // ^com + // com ] } @@ -71,7 +71,7 @@ module Wall = { let make = () => { [ // k. - // com + // ^com ] } } From 6b80247b7c9f6ec63ca5543f03a9e33cb5f1aadc Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 24 May 2025 11:55:08 +0200 Subject: [PATCH 03/20] Fix check in processLocalValue --- analysis/src/CompletionBackEnd.ml | 81 +++++++------------ .../src/DotPipeCompleteFromCurrentModule.res | 4 +- 2 files changed, 30 insertions(+), 55 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index b39f0997d6..1d63b81156 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -96,13 +96,6 @@ let completionForExportedModules ~env ~prefix ~exact ~namesUsed = {docstring = declared.docstring; module_ = declared.item}) let completionForExportedValues ~env ~prefix ~exact ~namesUsed = - (* dump the contents of env.QueryEnv.file.stamps *) - Stamps.iterValues - (fun stamp declared -> - Format.printf "stamp: %d name: %s loc: %s\n" stamp declared.name.txt - (Loc.toString declared.extentLoc)) - env.QueryEnv.file.stamps; - completionForExporteds (Exported.iter env.QueryEnv.exported Exported.Value) (Stamps.findValue env.file.stamps) ~prefix ~exact ~env ~namesUsed (fun declared -> Completion.Value declared.item) @@ -359,33 +352,36 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed let processLocalValue name loc contextPath scope ~prefix ~exact ~env ~(localTables : LocalTables.t) = - if Utils.checkName name ~prefix ~exact then + if Utils.checkName name ~prefix ~exact then ( print_endline ("processLocalValue name: " ^ name ^ " loc: " ^ Loc.toString loc); - match Hashtbl.find_opt localTables.valueTable (name, Loc.start loc) with - | Some declared -> - if not (Hashtbl.mem localTables.namesUsed name) then ( - Hashtbl.add localTables.namesUsed name (); + match Hashtbl.find_opt localTables.valueTable (name, Loc.start loc) with + | Some declared -> + if not (Hashtbl.mem localTables.namesUsed name) then ( + Hashtbl.add localTables.namesUsed name (); + localTables.resultRev <- + { + (Completion.create declared.name.txt ~env ~kind:(Value declared.item)) + with + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: localTables.resultRev) + | None -> + if !Cfg.debugFollowCtxPath then + Printf.printf "Completion Value Not Found %s loc:%s\n" name + (Loc.toString loc); localTables.resultRev <- - { - (Completion.create declared.name.txt ~env ~kind:(Value declared.item)) with - deprecated = declared.deprecated; - docstring = declared.docstring; - } + Completion.create name ~env + ~kind: + (match contextPath with + | Some contextPath -> FollowContextPath (contextPath, scope) + | None -> + Value + (Ctype.newconstr + (Path.Pident (Ident.create "Type Not Known")) + [])) :: localTables.resultRev) - | None -> - if !Cfg.debugFollowCtxPath then - Printf.printf "Completion Value Not Found %s loc:%s\n" name - (Loc.toString loc); - localTables.resultRev <- - Completion.create name ~env - ~kind: - (match contextPath with - | Some contextPath -> FollowContextPath (contextPath, scope) - | None -> - Value - (Ctype.newconstr (Path.Pident (Ident.create "Type Not Known")) [])) - :: localTables.resultRev let processLocalConstructor name loc ~prefix ~exact ~env ~(localTables : LocalTables.t) = @@ -474,20 +470,6 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) localTables |> LocalTables.populateConstructors ~env; localTables |> LocalTables.populateModules ~env; - (* dump the contents of localTables.valueTable *) - Hashtbl.iter - (fun (name, (l, c)) declared -> - Format.printf "name: %s loc: %d %d type: %s\n" name l c - (Shared.typeToString declared.Declared.item)) - localTables.valueTable; - - scope - |> List.iter (fun scope_item -> - match scope_item with - | ScopeTypes.Value (name, _, _, _) -> - Format.printf "scope_item: %s\n" name - | _ -> ()); - scope |> Scope.iterValuesBeforeFirstOpen (processLocalValue ~prefix ~exact ~env ~localTables); @@ -513,12 +495,6 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) |> Scope.iterModulesAfterFirstOpen (processLocalModule ~prefix ~exact ~env ~localTables); - (* dump the contents of localTables.valueTable *) - List.iter - (fun completion -> - Format.printf "resultRev name: %s\n" completion.Completion.name) - localTables.resultRev; - List.rev_append localTables.resultRev valuesFromOpens let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix @@ -1246,10 +1222,9 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact ~targetTypeId:mainTypeId in let ownIncludedModuleCompletions = - [] - (* completionsFromIncludedModule ~full ~env ~pos + completionsFromIncludedModule ~full ~env ~pos |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full - ~targetTypeId:mainTypeId *) + ~targetTypeId:mainTypeId in jsxCompletions @ pipeCompletions @ extraCompletions @ currentModuleCompletions @ ownIncludedModuleCompletions diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index 7addcf981a..eb78a6fb09 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -9,7 +9,7 @@ module Y = { let a = (x:t) => { // x. - // com + // ^com () } @@ -59,7 +59,7 @@ module Wall = { let make = () => { [ // k. - // com + // ^com ] } From 43b15e8e85e3be236bb28deb3350aa6c92b8f6ab Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 24 May 2025 12:00:24 +0200 Subject: [PATCH 04/20] Clean up dump logs --- analysis/src/CompletionBackEnd.ml | 27 ++----- .../DotPipeCompleteFromCurrentModule.res.txt | 74 +++++++++++++++++++ 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 1d63b81156..43d0d536ee 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -352,9 +352,7 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed let processLocalValue name loc contextPath scope ~prefix ~exact ~env ~(localTables : LocalTables.t) = - if Utils.checkName name ~prefix ~exact then ( - print_endline - ("processLocalValue name: " ^ name ^ " loc: " ^ Loc.toString loc); + if Utils.checkName name ~prefix ~exact then match Hashtbl.find_opt localTables.valueTable (name, Loc.start loc) with | Some declared -> if not (Hashtbl.mem localTables.namesUsed name) then ( @@ -381,7 +379,7 @@ let processLocalValue name loc contextPath scope ~prefix ~exact ~env (Ctype.newconstr (Path.Pident (Ident.create "Type Not Known")) [])) - :: localTables.resultRev) + :: localTables.resultRev let processLocalConstructor name loc ~prefix ~exact ~env ~(localTables : LocalTables.t) = @@ -564,13 +562,12 @@ let findLocalCompletionsForModules ~(localTables : LocalTables.t) ~env ~prefix let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens ~scope ~(completionContext : Completable.completionContext) = (* TODO: handle arbitrary interleaving of opens and local bindings correctly *) - print_endline + Log.log ("findLocalCompletionsWithOpens uri:" ^ Uri.toString env.file.uri ^ " pos:" ^ Pos.toString pos); let localTables = LocalTables.create () in match completionContext with | Value | ValueOrField -> - print_endline "Value or Field"; findLocalCompletionsForValuesAndConstructors ~localTables ~env ~prefix ~exact ~opens ~scope | Type -> @@ -616,16 +613,6 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~scope ~completionContext in - print_endline - ("localCompletionsWithOpens: " - ^ (List.map - (fun c -> - match c.Completion.sortText with - | None -> c.Completion.name - | Some insertText -> c.Completion.name ^ " " ^ insertText) - localCompletionsWithOpens - |> String.concat ",")); - let fileModules = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> @@ -789,7 +776,7 @@ let completionsGetCompletionType ~full completions = | _ -> None (** - Returns completions from the current module where `include OtherModule` is present. + Returns completions from the current module where `include OtherModule()` is present. *) let completionsFromIncludedModule ~full ~env ~pos = (* Get the path of the module where the cursor is.*) @@ -808,10 +795,8 @@ let completionsFromIncludedModule ~full ~env ~pos = let ownerPath = ModulePath.toPathWithPrefix ownerPath full.file.File.moduleName in - Format.printf "ownerPath: %s \n" (ownerPath |> String.concat "."); - if ownerPath = ownModule then ( - print_endline "hit"; - Some (Completion.create name.txt ~env ~kind:(Value item))) + if ownerPath = ownModule then + Some (Completion.create name.txt ~env ~kind:(Value item)) else None | _ -> None) diff --git a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt index 70f6773fec..973aff8871 100644 --- a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt +++ b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt @@ -30,3 +30,77 @@ Path }] }] +Complete src/DotPipeCompleteFromCurrentModule.res 60:17 +posCursor:[60:17] posNoWhite:[60:16] Found expr:[58:15->63:5] +posCursor:[60:17] posNoWhite:[60:16] Found expr:[59:8->62:9] +posCursor:[60:17] posNoWhite:[60:16] Found expr:[60:15->60:17] +Pexp_field [60:15->60:16] _:[62:8->60:17] +Completable: Cpath Value[k]."" +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[k]."" +ContextPath Value[k] +Path k +ContextPath Value[k]-> +ContextPath Value[k] +Path k +CPPipe pathFromEnv:Types found:true +Path Types. +Path +[{ + "label": "->addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null, + "sortText": "addPosFromVec2", + "insertText": "->addPosFromVec2", + "additionalTextEdits": [{ + "range": {"start": {"line": 60, "character": 16}, "end": {"line": 60, "character": 17}}, + "newText": "" + }] + }, { + "label": "->addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null, + "sortText": "addPos", + "insertText": "->addPos", + "additionalTextEdits": [{ + "range": {"start": {"line": 60, "character": 16}, "end": {"line": 60, "character": 17}}, + "newText": "" + }] + }] + +Complete src/DotPipeCompleteFromCurrentModule.res 72:21 +posCursor:[72:21] posNoWhite:[72:20] Found expr:[70:19->75:9] +posCursor:[72:21] posNoWhite:[72:20] Found expr:[71:12->74:13] +posCursor:[72:21] posNoWhite:[72:20] Found expr:[72:19->72:21] +Pexp_field [72:19->72:20] _:[74:12->72:21] +Completable: Cpath Value[k]."" +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[k]."" +ContextPath Value[k] +Path k +ContextPath Value[k]-> +ContextPath Value[k] +Path k +CPPipe pathFromEnv:Types found:true +Path Types. +Path +[{ + "label": "->addSprite", + "kind": 12, + "tags": [], + "detail": "(Types.context, string) => Types.comp", + "documentation": null, + "sortText": "addSprite", + "insertText": "->addSprite", + "additionalTextEdits": [{ + "range": {"start": {"line": 72, "character": 20}, "end": {"line": 72, "character": 21}}, + "newText": "" + }] + }] + From 5c82b2f9bbaa3425405da817c4bdf7585304a811 Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 24 May 2025 12:12:30 +0200 Subject: [PATCH 05/20] Include values without pipe completion as well --- analysis/src/CompletionBackEnd.ml | 62 +++++++++---------- .../src/DotPipeCompleteFromCurrentModule.res | 3 + .../DotPipeCompleteFromCurrentModule.res.txt | 42 ++++++++++--- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 43d0d536ee..fc656599ed 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -602,6 +602,31 @@ let getComplementaryCompletionsForTypedValue ~opens ~allFiles ~scope ~env prefix in localCompletionsWithOpens @ fileModules +(** + Returns completions from the current module where `include OtherModule()` is present. +*) +let completionsFromIncludedModule ~full ~env ~pos = + (* Get the path of the module where the cursor is.*) + let ownModule = TypeUtils.find_module_path_at_pos ~full ~pos in + + env.file.stamps |> Stamps.getEntries + |> List.filter_map (fun (_stamp, kind) -> + match kind with + | SharedTypes.Stamps.KValue + { + modulePath = + SharedTypes.ModulePath.IncludedModule (_path, ownerPath); + name; + item; + } -> + let ownerPath = + ModulePath.toPathWithPrefix ownerPath full.file.File.moduleName + in + if ownerPath = ownModule then + Some (Completion.create name.txt ~env ~kind:(Value item)) + else None + | _ -> None) + let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope ~completionContext ~env path = if debug then Printf.printf "Path %s\n" (path |> String.concat "."); @@ -613,6 +638,7 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~scope ~completionContext in + let includedCompletions = completionsFromIncludedModule ~full ~env ~pos in let fileModules = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> @@ -626,7 +652,7 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope (Completion.create name ~env ~kind:(Completion.FileModule name)) else None) in - localCompletionsWithOpens @ fileModules + localCompletionsWithOpens @ includedCompletions @ fileModules | moduleName :: path -> ( Log.log ("Path " ^ pathToString path); match @@ -775,31 +801,6 @@ let completionsGetCompletionType ~full completions = | Some {Completion.kind = ExtractedType (typ, _); env} -> Some (typ, env) | _ -> None -(** - Returns completions from the current module where `include OtherModule()` is present. -*) -let completionsFromIncludedModule ~full ~env ~pos = - (* Get the path of the module where the cursor is.*) - let ownModule = TypeUtils.find_module_path_at_pos ~full ~pos in - - env.file.stamps |> Stamps.getEntries - |> List.filter_map (fun (_stamp, kind) -> - match kind with - | SharedTypes.Stamps.KValue - { - modulePath = - SharedTypes.ModulePath.IncludedModule (_path, ownerPath); - name; - item; - } -> - let ownerPath = - ModulePath.toPathWithPrefix ownerPath full.file.File.moduleName - in - if ownerPath = ownModule then - Some (Completion.create name.txt ~env ~kind:(Value item)) - else None - | _ -> None) - let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos completions = let firstNonSyntheticCompletion = @@ -1200,20 +1201,15 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact ~full ~rawOpens typ else [] in + (* Add completions from the current module. *) let currentModuleCompletions = completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom ~opens:[] ~pos ~scope ~debug ~prefix ~env ~rawOpens ~full [] |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full ~targetTypeId:mainTypeId in - let ownIncludedModuleCompletions = - completionsFromIncludedModule ~full ~env ~pos - |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full - ~targetTypeId:mainTypeId - in jsxCompletions @ pipeCompletions @ extraCompletions - @ currentModuleCompletions @ ownIncludedModuleCompletions - @ globallyConfiguredCompletions)) + @ currentModuleCompletions @ globallyConfiguredCompletions)) | CTuple ctxPaths -> if Debug.verbose () then print_endline "[ctx_path]--> CTuple"; (* Turn a list of context paths into a list of type expressions. *) diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index eb78a6fb09..4ee56ca309 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -60,6 +60,9 @@ module Wall = { [ // k. // ^com + + // addV + // ^com ] } diff --git a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt index 973aff8871..44b37eecb3 100644 --- a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt +++ b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt @@ -31,10 +31,10 @@ Path }] Complete src/DotPipeCompleteFromCurrentModule.res 60:17 -posCursor:[60:17] posNoWhite:[60:16] Found expr:[58:15->63:5] -posCursor:[60:17] posNoWhite:[60:16] Found expr:[59:8->62:9] +posCursor:[60:17] posNoWhite:[60:16] Found expr:[58:15->66:5] +posCursor:[60:17] posNoWhite:[60:16] Found expr:[59:8->65:9] posCursor:[60:17] posNoWhite:[60:16] Found expr:[60:15->60:17] -Pexp_field [60:15->60:16] _:[62:8->60:17] +Pexp_field [60:15->60:16] _:[65:8->60:17] Completable: Cpath Value[k]."" Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -73,11 +73,35 @@ Path }] }] -Complete src/DotPipeCompleteFromCurrentModule.res 72:21 -posCursor:[72:21] posNoWhite:[72:20] Found expr:[70:19->75:9] -posCursor:[72:21] posNoWhite:[72:20] Found expr:[71:12->74:13] -posCursor:[72:21] posNoWhite:[72:20] Found expr:[72:19->72:21] -Pexp_field [72:19->72:20] _:[74:12->72:21] +Complete src/DotPipeCompleteFromCurrentModule.res 63:19 +posCursor:[63:19] posNoWhite:[63:18] Found expr:[58:15->66:5] +posCursor:[63:19] posNoWhite:[63:18] Found expr:[59:8->65:9] +posCursor:[63:19] posNoWhite:[63:18] Found expr:[63:15->63:19] +Pexp_ident addV:[63:15->63:19] +Completable: Cpath Value[addV] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[addV] +Path addV +[{ + "label": "addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null + }, { + "label": "addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null + }] + +Complete src/DotPipeCompleteFromCurrentModule.res 75:21 +posCursor:[75:21] posNoWhite:[75:20] Found expr:[73:19->78:9] +posCursor:[75:21] posNoWhite:[75:20] Found expr:[74:12->77:13] +posCursor:[75:21] posNoWhite:[75:20] Found expr:[75:19->75:21] +Pexp_field [75:19->75:20] _:[77:12->75:21] Completable: Cpath Value[k]."" Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -99,7 +123,7 @@ Path "sortText": "addSprite", "insertText": "->addSprite", "additionalTextEdits": [{ - "range": {"start": {"line": 72, "character": 20}, "end": {"line": 72, "character": 21}}, + "range": {"start": {"line": 75, "character": 20}, "end": {"line": 75, "character": 21}}, "newText": "" }] }] From 8fdc591b2e7b8eaaedd677ce2e5a7cea97745b8f Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 24 May 2025 18:46:23 +0200 Subject: [PATCH 06/20] Introduce include scope --- analysis/src/CompletionBackEnd.ml | 65 ++++++++------ analysis/src/CompletionFrontEnd.ml | 6 ++ analysis/src/Scope.ml | 26 ++++++ analysis/src/SharedTypes.ml | 1 + .../src/DotPipeCompleteFromCurrentModule.res | 6 +- .../DotPipeCompleteFromCurrentModule.res.txt | 90 ++++++++++++++----- 6 files changed, 144 insertions(+), 50 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index fc656599ed..dfbfc34922 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -451,6 +451,36 @@ let processLocalModule name loc ~prefix ~exact ~env (Printf.sprintf "Completion Module Not Found %s loc:%s\n" name (Loc.toString loc)) +let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) + ~(localTables : LocalTables.t) = + (* process only values for now *) + localTables.valueTable + |> Hashtbl.iter (fun (name, _) (declared : Types.type_expr Declared.t) -> + (* We check all the values if their origin is the same as the include path. *) + match declared.modulePath with + | SharedTypes.ModulePath.IncludedModule (source, _) -> + let source_module_path = + match Path.flatten source with + | `Contains_apply -> "" + | `Ok (ident, path) -> ident.name :: path |> String.concat "." + in + + if source_module_path = includePath then + (* If this is the case we perform a similar check for the prefix *) + if Utils.checkName name ~prefix ~exact then + if not (Hashtbl.mem localTables.namesUsed name) then ( + Hashtbl.add localTables.namesUsed name (); + localTables.resultRev <- + { + (Completion.create declared.name.txt ~env + ~kind:(Value declared.item)) + with + deprecated = declared.deprecated; + docstring = declared.docstring; + } + :: localTables.resultRev) + | _ -> ()) + let getItemsFromOpens ~opens ~localTables ~prefix ~exact ~completionContext = opens |> List.fold_left @@ -477,6 +507,9 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) scope |> Scope.iterModulesBeforeFirstOpen (processLocalModule ~prefix ~exact ~env ~localTables); + scope + |> Scope.iterIncludesBeforeFirstOpen + (processLocalInclude ~prefix ~exact ~env ~localTables); let valuesFromOpens = getItemsFromOpens ~opens ~localTables ~prefix ~exact @@ -493,6 +526,10 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) |> Scope.iterModulesAfterFirstOpen (processLocalModule ~prefix ~exact ~env ~localTables); + scope + |> Scope.iterIncludesAfterFirstOpen + (processLocalInclude ~prefix ~exact ~env ~localTables); + List.rev_append localTables.resultRev valuesFromOpens let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix @@ -602,31 +639,6 @@ let getComplementaryCompletionsForTypedValue ~opens ~allFiles ~scope ~env prefix in localCompletionsWithOpens @ fileModules -(** - Returns completions from the current module where `include OtherModule()` is present. -*) -let completionsFromIncludedModule ~full ~env ~pos = - (* Get the path of the module where the cursor is.*) - let ownModule = TypeUtils.find_module_path_at_pos ~full ~pos in - - env.file.stamps |> Stamps.getEntries - |> List.filter_map (fun (_stamp, kind) -> - match kind with - | SharedTypes.Stamps.KValue - { - modulePath = - SharedTypes.ModulePath.IncludedModule (_path, ownerPath); - name; - item; - } -> - let ownerPath = - ModulePath.toPathWithPrefix ownerPath full.file.File.moduleName - in - if ownerPath = ownModule then - Some (Completion.create name.txt ~env ~kind:(Value item)) - else None - | _ -> None) - let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope ~completionContext ~env path = if debug then Printf.printf "Path %s\n" (path |> String.concat "."); @@ -638,7 +650,6 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope findLocalCompletionsWithOpens ~pos ~env ~prefix ~exact ~opens ~scope ~completionContext in - let includedCompletions = completionsFromIncludedModule ~full ~env ~pos in let fileModules = allFiles |> FileSet.elements |> Utils.filterMap (fun name -> @@ -652,7 +663,7 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope (Completion.create name ~env ~kind:(Completion.FileModule name)) else None) in - localCompletionsWithOpens @ includedCompletions @ fileModules + localCompletionsWithOpens @ fileModules | moduleName :: path -> ( Log.log ("Path " ^ pathToString path); match diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 2c2b49380e..17db58be12 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -744,6 +744,12 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor mbs |> List.iter scopeModuleBinding; mbs |> List.iter (fun b -> iterator.module_binding iterator b); processed := true + | Pstr_include {pincl_mod = {pmod_desc = med}} -> ( + match med with + | Pmod_apply ({pmod_desc = Pmod_ident {txt = lid; loc}}, _) -> + let module_name = Longident.flatten lid |> String.concat "." in + scope := !scope |> Scope.addInclude ~name:module_name ~loc + | _ -> ()) | _ -> ()); if not !processed then Ast_iterator.default_iterator.structure_item iterator item diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index 0e092d2a43..d55dd65206 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -14,6 +14,7 @@ let itemToString item = | Module (s, loc) -> "Module " ^ s ^ " " ^ Loc.toString loc | Value (s, loc, _, _) -> "Value " ^ s ^ " " ^ Loc.toString loc | Type (s, loc) -> "Type " ^ s ^ " " ^ Loc.toString loc + | Include (s, loc) -> "Include " ^ s ^ " " ^ Loc.toString loc [@@live] let create () : t = [] @@ -32,6 +33,7 @@ let addValue ~name ~loc ?contextPath x = (SharedTypes.Completable.contextPathToString contextPath)); Value (name, loc, contextPath, x) :: x let addType ~name ~loc x = Type (name, loc) :: x +let addInclude ~name ~loc x = Include (name, loc) :: x let iterValuesBeforeFirstOpen f x = let rec loop items = @@ -129,6 +131,30 @@ let iterModulesAfterFirstOpen f x = in loop false x +let iterIncludesBeforeFirstOpen f x = + let rec loop items = + match items with + | Include (s, loc) :: rest -> + f s loc; + loop rest + | Open _ :: _ -> () + | _ :: rest -> loop rest + | [] -> () + in + loop x + +let iterIncludesAfterFirstOpen f x = + let rec loop foundOpen items = + match items with + | Include (s, loc) :: rest -> + if foundOpen then f s loc; + loop foundOpen rest + | Open _ :: rest -> loop true rest + | _ :: rest -> loop foundOpen rest + | [] -> () + in + loop false x + let getRawOpens x = x |> Utils.filterMap (function diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index fb7be4e58c..02bda567b7 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -800,6 +800,7 @@ module ScopeTypes = struct | Open of string list | Type of string * Location.t | Value of string * Location.t * Completable.contextPath option * item list + | Include of string * Location.t end module Completion = struct diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index 4ee56ca309..694ab25549 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -56,13 +56,15 @@ module Wall = { include PosComp({ type t = t }) + let blah = (k: Types.context) => "" + let make = () => { [ // k. // ^com - // addV - // ^com + // add + // ^com ] } diff --git a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt index 44b37eecb3..2aa7ce2f5c 100644 --- a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt +++ b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt @@ -30,11 +30,11 @@ Path }] }] -Complete src/DotPipeCompleteFromCurrentModule.res 60:17 -posCursor:[60:17] posNoWhite:[60:16] Found expr:[58:15->66:5] -posCursor:[60:17] posNoWhite:[60:16] Found expr:[59:8->65:9] -posCursor:[60:17] posNoWhite:[60:16] Found expr:[60:15->60:17] -Pexp_field [60:15->60:16] _:[65:8->60:17] +Complete src/DotPipeCompleteFromCurrentModule.res 62:17 +posCursor:[62:17] posNoWhite:[62:16] Found expr:[60:15->68:5] +posCursor:[62:17] posNoWhite:[62:16] Found expr:[61:8->67:9] +posCursor:[62:17] posNoWhite:[62:16] Found expr:[62:15->62:17] +Pexp_field [62:15->62:16] _:[67:8->62:17] Completable: Cpath Value[k]."" Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -48,6 +48,18 @@ CPPipe pathFromEnv:Types found:true Path Types. Path [{ + "label": "->blah", + "kind": 12, + "tags": [], + "detail": "Types.context => string", + "documentation": null, + "sortText": "blah", + "insertText": "->blah", + "additionalTextEdits": [{ + "range": {"start": {"line": 62, "character": 16}, "end": {"line": 62, "character": 17}}, + "newText": "" + }] + }, { "label": "->addPosFromVec2", "kind": 12, "tags": [], @@ -56,7 +68,7 @@ Path "sortText": "addPosFromVec2", "insertText": "->addPosFromVec2", "additionalTextEdits": [{ - "range": {"start": {"line": 60, "character": 16}, "end": {"line": 60, "character": 17}}, + "range": {"start": {"line": 62, "character": 16}, "end": {"line": 62, "character": 17}}, "newText": "" }] }, { @@ -68,21 +80,21 @@ Path "sortText": "addPos", "insertText": "->addPos", "additionalTextEdits": [{ - "range": {"start": {"line": 60, "character": 16}, "end": {"line": 60, "character": 17}}, + "range": {"start": {"line": 62, "character": 16}, "end": {"line": 62, "character": 17}}, "newText": "" }] }] -Complete src/DotPipeCompleteFromCurrentModule.res 63:19 -posCursor:[63:19] posNoWhite:[63:18] Found expr:[58:15->66:5] -posCursor:[63:19] posNoWhite:[63:18] Found expr:[59:8->65:9] -posCursor:[63:19] posNoWhite:[63:18] Found expr:[63:15->63:19] -Pexp_ident addV:[63:15->63:19] -Completable: Cpath Value[addV] +Complete src/DotPipeCompleteFromCurrentModule.res 65:18 +posCursor:[65:18] posNoWhite:[65:17] Found expr:[60:15->68:5] +posCursor:[65:18] posNoWhite:[65:17] Found expr:[61:8->67:9] +posCursor:[65:18] posNoWhite:[65:17] Found expr:[65:15->65:18] +Pexp_ident add:[65:15->65:18] +Completable: Cpath Value[add] Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib -ContextPath Value[addV] -Path addV +ContextPath Value[add] +Path add [{ "label": "addPosFromVec2", "kind": 12, @@ -97,11 +109,11 @@ Path addV "documentation": null }] -Complete src/DotPipeCompleteFromCurrentModule.res 75:21 -posCursor:[75:21] posNoWhite:[75:20] Found expr:[73:19->78:9] -posCursor:[75:21] posNoWhite:[75:20] Found expr:[74:12->77:13] -posCursor:[75:21] posNoWhite:[75:20] Found expr:[75:19->75:21] -Pexp_field [75:19->75:20] _:[77:12->75:21] +Complete src/DotPipeCompleteFromCurrentModule.res 77:21 +posCursor:[77:21] posNoWhite:[77:20] Found expr:[75:19->80:9] +posCursor:[77:21] posNoWhite:[77:20] Found expr:[76:12->79:13] +posCursor:[77:21] posNoWhite:[77:20] Found expr:[77:19->77:21] +Pexp_field [77:19->77:20] _:[79:12->77:21] Completable: Cpath Value[k]."" Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -115,6 +127,18 @@ CPPipe pathFromEnv:Types found:true Path Types. Path [{ + "label": "->blah", + "kind": 12, + "tags": [], + "detail": "Types.context => string", + "documentation": null, + "sortText": "blah", + "insertText": "->blah", + "additionalTextEdits": [{ + "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "newText": "" + }] + }, { "label": "->addSprite", "kind": 12, "tags": [], @@ -123,7 +147,31 @@ Path "sortText": "addSprite", "insertText": "->addSprite", "additionalTextEdits": [{ - "range": {"start": {"line": 75, "character": 20}, "end": {"line": 75, "character": 21}}, + "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "newText": "" + }] + }, { + "label": "->addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null, + "sortText": "addPosFromVec2", + "insertText": "->addPosFromVec2", + "additionalTextEdits": [{ + "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "newText": "" + }] + }, { + "label": "->addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null, + "sortText": "addPos", + "insertText": "->addPos", + "additionalTextEdits": [{ + "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, "newText": "" }] }] From f849598b76e68f97afad0cab7c71c74d41b75b78 Mon Sep 17 00:00:00 2001 From: nojaf Date: Sun, 25 May 2025 12:34:51 +0200 Subject: [PATCH 07/20] Check if include ends with path --- analysis/src/CompletionBackEnd.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index dfbfc34922..d27adbf70a 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -465,7 +465,7 @@ let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) | `Ok (ident, path) -> ident.name :: path |> String.concat "." in - if source_module_path = includePath then + if String.ends_with ~suffix:includePath source_module_path then (* If this is the case we perform a similar check for the prefix *) if Utils.checkName name ~prefix ~exact then if not (Hashtbl.mem localTables.namesUsed name) then ( From 7b873208f5e0bdfb8288b87ae1bb7da139b6471a Mon Sep 17 00:00:00 2001 From: nojaf Date: Sun, 25 May 2025 16:20:48 +0200 Subject: [PATCH 08/20] Deal with namespace in modulePathFromEnv --- analysis/src/TypeUtils.ml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index f1547b4a82..54346caa9d 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1,6 +1,20 @@ open SharedTypes -let modulePathFromEnv env = env.QueryEnv.file.moduleName :: List.rev env.pathRev +let modulePathFromEnv env = + let moduleName = env.QueryEnv.file.moduleName in + let transformedModuleName = + (* Transform namespaced module names from internal format (Context-Kaplay) + to user-facing format (Kaplay.Context) *) + match String.rindex_opt moduleName '-' with + | None -> moduleName + | Some i -> + let namespace = + String.sub moduleName (i + 1) (String.length moduleName - i - 1) + in + let module_ = String.sub moduleName 0 i in + namespace ^ "." ^ module_ + in + transformedModuleName :: List.rev env.pathRev let fullTypeIdFromDecl ~env ~name ~modulePath = env.QueryEnv.file.moduleName :: ModulePath.toPath modulePath name From 88cd2bc9d2ed0ba66bd4fbf9ea0c0c42cf6c9bcd Mon Sep 17 00:00:00 2001 From: nojaf Date: Sun, 25 May 2025 16:21:31 +0200 Subject: [PATCH 09/20] Simplify completions from current module --- analysis/src/CompletionBackEnd.ml | 13 ++++++------- analysis/src/Scope.ml | 17 ++--------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index d27adbf70a..caffedfed0 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -477,6 +477,7 @@ let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) with deprecated = declared.deprecated; docstring = declared.docstring; + synthetic = true; } :: localTables.resultRev) | _ -> ()) @@ -507,9 +508,6 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) scope |> Scope.iterModulesBeforeFirstOpen (processLocalModule ~prefix ~exact ~env ~localTables); - scope - |> Scope.iterIncludesBeforeFirstOpen - (processLocalInclude ~prefix ~exact ~env ~localTables); let valuesFromOpens = getItemsFromOpens ~opens ~localTables ~prefix ~exact @@ -527,8 +525,7 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) (processLocalModule ~prefix ~exact ~env ~localTables); scope - |> Scope.iterIncludesAfterFirstOpen - (processLocalInclude ~prefix ~exact ~env ~localTables); + |> Scope.iterIncludes (processLocalInclude ~prefix ~exact ~env ~localTables); List.rev_append localTables.resultRev valuesFromOpens @@ -1088,6 +1085,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact | None -> []) | CPPipe {contextPath = cp; id = prefix; lhsLoc; inJsx; synthetic} -> ( if Debug.verbose () then print_endline "[ctx_path]--> CPPipe"; + (* The environment at the cursor is the environment we're completing from. *) + let env_at_cursor = env in match cp |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env @@ -1214,8 +1213,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact in (* Add completions from the current module. *) let currentModuleCompletions = - completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom - ~opens:[] ~pos ~scope ~debug ~prefix ~env ~rawOpens ~full [] + getCompletionsForPath ~debug ~completionContext:Value ~exact:false + ~opens:[] ~full ~pos ~env:env_at_cursor ~scope [prefix] |> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full ~targetTypeId:mainTypeId in diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index d55dd65206..33e850bc02 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -131,30 +131,17 @@ let iterModulesAfterFirstOpen f x = in loop false x -let iterIncludesBeforeFirstOpen f x = +let iterIncludes f x = let rec loop items = match items with + | [] -> () | Include (s, loc) :: rest -> f s loc; loop rest - | Open _ :: _ -> () | _ :: rest -> loop rest - | [] -> () in loop x -let iterIncludesAfterFirstOpen f x = - let rec loop foundOpen items = - match items with - | Include (s, loc) :: rest -> - if foundOpen then f s loc; - loop foundOpen rest - | Open _ :: rest -> loop true rest - | _ :: rest -> loop foundOpen rest - | [] -> () - in - loop false x - let getRawOpens x = x |> Utils.filterMap (function From 8883bfd239398d009b698e27288b9a6fa1eee915 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 27 May 2025 11:12:02 +0200 Subject: [PATCH 10/20] Also iter includes in findLocalCompletionsForValues --- analysis/src/CompletionBackEnd.ml | 4 + .../src/DotPipeCompleteFromCurrentModule.res | 11 +++ .../DotPipeCompleteFromCurrentModule.res.txt | 97 ++++++++++++++----- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index caffedfed0..512d397ffd 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -551,6 +551,10 @@ let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix scope |> Scope.iterModulesAfterFirstOpen (processLocalModule ~prefix ~exact ~env ~localTables); + + scope + |> Scope.iterIncludes (processLocalInclude ~prefix ~exact ~env ~localTables); + List.rev_append localTables.resultRev valuesFromOpens let findLocalCompletionsForTypes ~(localTables : LocalTables.t) ~env ~prefix diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index 694ab25549..039dbf9c5f 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -51,6 +51,9 @@ module SpriteComp = ( external k: Types.context = "k" +@send +external add: (Types.context, array) => 't = "add" + module Wall = { type t @@ -68,6 +71,14 @@ module Wall = { ] } + let makeWith = (x) => { + k->add([ + k->addPos(1.0, 2.0), + // addP + // ^com + ]) + } + module Poster = { type t diff --git a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt index 2aa7ce2f5c..786330a6cf 100644 --- a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt +++ b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt @@ -30,11 +30,11 @@ Path }] }] -Complete src/DotPipeCompleteFromCurrentModule.res 62:17 -posCursor:[62:17] posNoWhite:[62:16] Found expr:[60:15->68:5] -posCursor:[62:17] posNoWhite:[62:16] Found expr:[61:8->67:9] -posCursor:[62:17] posNoWhite:[62:16] Found expr:[62:15->62:17] -Pexp_field [62:15->62:16] _:[67:8->62:17] +Complete src/DotPipeCompleteFromCurrentModule.res 65:17 +posCursor:[65:17] posNoWhite:[65:16] Found expr:[63:15->71:5] +posCursor:[65:17] posNoWhite:[65:16] Found expr:[64:8->70:9] +posCursor:[65:17] posNoWhite:[65:16] Found expr:[65:15->65:17] +Pexp_field [65:15->65:16] _:[70:8->65:17] Completable: Cpath Value[k]."" Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -56,7 +56,19 @@ Path "sortText": "blah", "insertText": "->blah", "additionalTextEdits": [{ - "range": {"start": {"line": 62, "character": 16}, "end": {"line": 62, "character": 17}}, + "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, + "newText": "" + }] + }, { + "label": "->add", + "kind": 12, + "tags": [], + "detail": "(Types.context, array) => 't", + "documentation": null, + "sortText": "add", + "insertText": "->add", + "additionalTextEdits": [{ + "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, "newText": "" }] }, { @@ -68,7 +80,7 @@ Path "sortText": "addPosFromVec2", "insertText": "->addPosFromVec2", "additionalTextEdits": [{ - "range": {"start": {"line": 62, "character": 16}, "end": {"line": 62, "character": 17}}, + "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, "newText": "" }] }, { @@ -80,21 +92,50 @@ Path "sortText": "addPos", "insertText": "->addPos", "additionalTextEdits": [{ - "range": {"start": {"line": 62, "character": 16}, "end": {"line": 62, "character": 17}}, + "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, "newText": "" }] }] -Complete src/DotPipeCompleteFromCurrentModule.res 65:18 -posCursor:[65:18] posNoWhite:[65:17] Found expr:[60:15->68:5] -posCursor:[65:18] posNoWhite:[65:17] Found expr:[61:8->67:9] -posCursor:[65:18] posNoWhite:[65:17] Found expr:[65:15->65:18] -Pexp_ident add:[65:15->65:18] +Complete src/DotPipeCompleteFromCurrentModule.res 68:18 +posCursor:[68:18] posNoWhite:[68:17] Found expr:[63:15->71:5] +posCursor:[68:18] posNoWhite:[68:17] Found expr:[64:8->70:9] +posCursor:[68:18] posNoWhite:[68:17] Found expr:[68:15->68:18] +Pexp_ident add:[68:15->68:18] Completable: Cpath Value[add] Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath Value[add] Path add +[{ + "label": "add", + "kind": 12, + "tags": [], + "detail": "(Types.context, array) => 't", + "documentation": null + }, { + "label": "addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null + }, { + "label": "addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null + }] + +Complete src/DotPipeCompleteFromCurrentModule.res 76:19 +posCursor:[76:19] posNoWhite:[76:18] Found expr:[73:19->79:5] +posCursor:[76:19] posNoWhite:[76:18] Found expr:[74:8->78:10] +Completable: Cexpression CArgument Value[add]($1)=addP->array +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath CArgument Value[add]($1) +ContextPath Value[add] +Path add [{ "label": "addPosFromVec2", "kind": 12, @@ -109,11 +150,11 @@ Path add "documentation": null }] -Complete src/DotPipeCompleteFromCurrentModule.res 77:21 -posCursor:[77:21] posNoWhite:[77:20] Found expr:[75:19->80:9] -posCursor:[77:21] posNoWhite:[77:20] Found expr:[76:12->79:13] -posCursor:[77:21] posNoWhite:[77:20] Found expr:[77:19->77:21] -Pexp_field [77:19->77:20] _:[79:12->77:21] +Complete src/DotPipeCompleteFromCurrentModule.res 88:21 +posCursor:[88:21] posNoWhite:[88:20] Found expr:[86:19->91:9] +posCursor:[88:21] posNoWhite:[88:20] Found expr:[87:12->90:13] +posCursor:[88:21] posNoWhite:[88:20] Found expr:[88:19->88:21] +Pexp_field [88:19->88:20] _:[90:12->88:21] Completable: Cpath Value[k]."" Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -135,7 +176,19 @@ Path "sortText": "blah", "insertText": "->blah", "additionalTextEdits": [{ - "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, + "newText": "" + }] + }, { + "label": "->add", + "kind": 12, + "tags": [], + "detail": "(Types.context, array) => 't", + "documentation": null, + "sortText": "add", + "insertText": "->add", + "additionalTextEdits": [{ + "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, "newText": "" }] }, { @@ -147,7 +200,7 @@ Path "sortText": "addSprite", "insertText": "->addSprite", "additionalTextEdits": [{ - "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, "newText": "" }] }, { @@ -159,7 +212,7 @@ Path "sortText": "addPosFromVec2", "insertText": "->addPosFromVec2", "additionalTextEdits": [{ - "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, "newText": "" }] }, { @@ -171,7 +224,7 @@ Path "sortText": "addPos", "insertText": "->addPos", "additionalTextEdits": [{ - "range": {"start": {"line": 77, "character": 20}, "end": {"line": 77, "character": 21}}, + "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, "newText": "" }] }] From 978811d2e3d4739406b18ee30a334870371a45f6 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 27 May 2025 11:18:38 +0200 Subject: [PATCH 11/20] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bced876653..7f653f1065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Complete from `RegExp` stdlib module for regexes. https://github.com/rescript-lang/rescript/pull/7425 - Allow oneliner formatting when including module with single type alias. https://github.com/rescript-lang/rescript/pull/7502 - Improve error messages for JSX type mismatches, passing objects where record is expected, passing array literal where tuple is expected, and more. https://github.com/rescript-lang/rescript/pull/7500 +- Editor: add completions from included modules. https://github.com/rescript-lang/rescript/pull/7515 # 12.0.0-alpha.13 From c4bdd038b8459770c3b579ed62c3181f877b3cbc Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 28 May 2025 09:56:22 +0200 Subject: [PATCH 12/20] Remove unused function --- analysis/src/TypeUtils.ml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 54346caa9d..99ffca3f28 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1285,22 +1285,3 @@ let completionPathFromMaybeBuiltin path = (* Route Stdlib_X to Stdlib.X for proper completions without the Stdlib_ prefix *) Some (String.split_on_char '_' mainModule) | _ -> None) - -let find_module_path_at_pos ~(full : SharedTypes.full) ~(pos : int * int) = - let rec aux (structure : Module.structure) (path_acc : string list) = - let found = - structure.Module.items - |> List.find_map (fun item -> - match item.Module.kind with - | SharedTypes.Module.Module {type_ = Structure substructure; _} -> - let loc = item.loc in - if CursorPosition.locHasCursor loc ~pos then - Some (aux substructure (path_acc @ [item.name])) - else None - | _ -> None) - in - match found with - | Some path -> path - | None -> path_acc - in - aux full.file.File.structure [full.file.File.moduleName] From 6aa9d71e29340020a505be2183e67dd69efd46ef Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 10:47:51 +0200 Subject: [PATCH 13/20] Dump structure --- analysis/src/CmtViewer.ml | 33 ++++++++++++++++++++++++++++++++- compiler/ml/path.ml | 5 +++++ compiler/ml/path.mli | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/analysis/src/CmtViewer.ml b/analysis/src/CmtViewer.ml index b41d97ef72..1af0a87c11 100644 --- a/analysis/src/CmtViewer.ml +++ b/analysis/src/CmtViewer.ml @@ -1,5 +1,5 @@ let loc_to_string (loc : Warnings.loc) : string = - Format.sprintf "(%03d,%03d--%03d,%03d)" loc.loc_start.pos_lnum + Format.sprintf "(%02d,%02d--%02d,%02d)" loc.loc_start.pos_lnum (loc.loc_start.pos_cnum - loc.loc_start.pos_bol) loc.loc_end.pos_lnum (loc.loc_end.pos_cnum - loc.loc_end.pos_bol) @@ -47,6 +47,9 @@ let dump ?filter rescript_json cmt_path = | Some (Cursor (line, col)) -> Printf.printf "Filtering by cursor %d,%d\n" line col | Some (Loc loc) -> Printf.printf "Filtering by loc %s\n" (Loc.toString loc)); + + Printf.printf "file moduleName: %s\n\n" full.file.moduleName; + let stamps = full.file.stamps |> getEntries |> List.filter (fun (_, stamp) -> applyFilter (locOfKind stamp)) @@ -78,6 +81,34 @@ let dump ?filter rescript_json cmt_path = Printf.printf "%d kconstructor %s\n" stamp (loc_to_string t.extentLoc)); + (* dump the structure *) + let rec dump_structure indent (structure : Module.structure) = + if indent > 0 then Printf.printf "%s" (String.make indent ' '); + Printf.printf "Structure %s:\n" structure.name; + structure.items |> List.iter (dump_structure_item (indent + 2)) + and dump_structure_item indent item = + if indent > 0 then Printf.printf "%s" (String.make indent ' '); + let open Module in + match item.kind with + | Value _typedExpr -> + Printf.printf "Value %s %s\n" item.name (loc_to_string item.loc) + | Type _ -> + Printf.printf "Type %s %s\n" item.name (loc_to_string item.loc) + | Module {type_ = m} -> + Printf.printf "Module %s %s\n" item.name (loc_to_string item.loc); + dump_module indent m + and dump_module indent (module_ : Module.t) = + match module_ with + | Ident path -> Printf.printf "Module (Ident) %s\n" (Path.to_string path) + | Structure structure -> dump_structure indent structure + | Constraint (m1, m2) -> + dump_module indent m1; + dump_module indent m2 + in + + print_newline (); + dump_structure 0 full.file.structure; + (* Dump all locItems (typed nodes) *) let locItems = match full.extra with diff --git a/compiler/ml/path.ml b/compiler/ml/path.ml index d3c78a3b13..2fde12cb38 100644 --- a/compiler/ml/path.ml +++ b/compiler/ml/path.ml @@ -104,3 +104,8 @@ let is_constructor_typath p = match constructor_typath p with | Regular _ -> false | _ -> true + +let rec to_string = function + | Pident id -> Ident.name id + | Pdot (p, s, _) -> to_string p ^ "." ^ s + | Papply (p1, p2) -> to_string p1 ^ "(" ^ to_string p2 ^ ")" diff --git a/compiler/ml/path.mli b/compiler/ml/path.mli index 0c24ae12f3..6ae0ed4f99 100644 --- a/compiler/ml/path.mli +++ b/compiler/ml/path.mli @@ -42,3 +42,4 @@ type typath = val constructor_typath : t -> typath val is_constructor_typath : t -> bool +val to_string : t -> string From feb9b1e51ac251fd231676a58f9fc9265fae92c3 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 11:03:57 +0200 Subject: [PATCH 14/20] Print scope --- analysis/src/CmtViewer.ml | 24 ++++++++++-------------- analysis/src/Completions.ml | 7 +++++++ analysis/src/SharedTypes.ml | 12 ++++++++++++ compiler/ext/warnings.ml | 6 ++++++ compiler/ext/warnings.mli | 5 +++++ 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/analysis/src/CmtViewer.ml b/analysis/src/CmtViewer.ml index 1af0a87c11..6d2aa77ef5 100644 --- a/analysis/src/CmtViewer.ml +++ b/analysis/src/CmtViewer.ml @@ -1,9 +1,3 @@ -let loc_to_string (loc : Warnings.loc) : string = - Format.sprintf "(%02d,%02d--%02d,%02d)" loc.loc_start.pos_lnum - (loc.loc_start.pos_cnum - loc.loc_start.pos_bol) - loc.loc_end.pos_lnum - (loc.loc_end.pos_cnum - loc.loc_end.pos_bol) - let filter_by_cursor cursor (loc : Warnings.loc) : bool = match cursor with | None -> true @@ -70,16 +64,16 @@ let dump ?filter rescript_json cmt_path = match kind with | KType t -> Printf.printf "%d ktype %s\n" stamp - (loc_to_string t.extentLoc) + (Warnings.loc_to_string t.extentLoc) | KValue t -> Printf.printf "%d kvalue %s\n" stamp - (loc_to_string t.extentLoc) + (Warnings.loc_to_string t.extentLoc) | KModule t -> Printf.printf "%d kmodule %s\n" stamp - (loc_to_string t.extentLoc) + (Warnings.loc_to_string t.extentLoc) | KConstructor t -> Printf.printf "%d kconstructor %s\n" stamp - (loc_to_string t.extentLoc)); + (Warnings.loc_to_string t.extentLoc)); (* dump the structure *) let rec dump_structure indent (structure : Module.structure) = @@ -91,11 +85,13 @@ let dump ?filter rescript_json cmt_path = let open Module in match item.kind with | Value _typedExpr -> - Printf.printf "Value %s %s\n" item.name (loc_to_string item.loc) + Printf.printf "Value %s %s\n" item.name + (Warnings.loc_to_string item.loc) | Type _ -> - Printf.printf "Type %s %s\n" item.name (loc_to_string item.loc) + Printf.printf "Type %s %s\n" item.name (Warnings.loc_to_string item.loc) | Module {type_ = m} -> - Printf.printf "Module %s %s\n" item.name (loc_to_string item.loc); + Printf.printf "Module %s %s\n" item.name + (Warnings.loc_to_string item.loc); dump_module indent m and dump_module indent (module_ : Module.t) = match module_ with @@ -127,6 +123,6 @@ let dump ?filter rescript_json cmt_path = | 0 -> compare aLoc.pos_cnum bLoc.pos_cnum | c -> c) |> List.iter (fun {loc; locType} -> - let locStr = loc_to_string loc in + let locStr = Warnings.loc_to_string loc in let kindStr = SharedTypes.locTypeToString locType in Printf.printf "%s %s\n" locStr kindStr) diff --git a/analysis/src/Completions.ml b/analysis/src/Completions.ml index 42176bb3b0..a3db6de8e1 100644 --- a/analysis/src/Completions.ml +++ b/analysis/src/Completions.ml @@ -9,6 +9,13 @@ let getCompletions ~debug ~path ~pos ~currentFile ~forHover = with | None -> None | Some (completable, scope) -> ( + if debug then ( + Printf.printf "\nScope from frontend:\n"; + List.iter + (fun item -> + Printf.printf "%s\n" (SharedTypes.ScopeTypes.item_to_string item)) + scope; + print_newline ()); (* Only perform expensive ast operations if there are completables *) match Cmt.loadFullCmtFromPath ~path with | None -> None diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 02bda567b7..73d43ffc39 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -801,6 +801,18 @@ module ScopeTypes = struct | Type of string * Location.t | Value of string * Location.t * Completable.contextPath option * item list | Include of string * Location.t + + let item_to_string = function + | Constructor (name, loc) -> + "Constructor " ^ name ^ " " ^ Warnings.loc_to_string loc + | Field (name, loc) -> "Field " ^ name ^ " " ^ Warnings.loc_to_string loc + | Module (name, loc) -> "Module " ^ name ^ " " ^ Warnings.loc_to_string loc + | Open path -> "Open " ^ (path |> String.concat ".") + | Type (name, loc) -> "Type " ^ name ^ " " ^ Warnings.loc_to_string loc + | Value (name, loc, _, _) -> + "Value " ^ name ^ " " ^ Warnings.loc_to_string loc + | Include (name, loc) -> + "Include " ^ name ^ " " ^ Warnings.loc_to_string loc end module Completion = struct diff --git a/compiler/ext/warnings.ml b/compiler/ext/warnings.ml index fd3d506411..f799100a36 100644 --- a/compiler/ext/warnings.ml +++ b/compiler/ext/warnings.ml @@ -676,3 +676,9 @@ let help_warnings () = (String.concat ", " (List.map string_of_int l)) done; exit 0 + +let loc_to_string (loc : loc) : string = + Format.sprintf "(%02d,%02d--%02d,%02d)" loc.loc_start.pos_lnum + (loc.loc_start.pos_cnum - loc.loc_start.pos_bol) + loc.loc_end.pos_lnum + (loc.loc_end.pos_cnum - loc.loc_end.pos_bol) diff --git a/compiler/ext/warnings.mli b/compiler/ext/warnings.mli index 4b96f0f427..59971b94be 100644 --- a/compiler/ext/warnings.mli +++ b/compiler/ext/warnings.mli @@ -126,3 +126,8 @@ val message : t -> string val number : t -> int val reset : unit -> unit + +val loc_to_string : loc -> string +(** +Turn the location into a string with (line,column--line,column) format. +*) From fa55ecef79ff78f48ff91174a0bbe052311bc8a9 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 12:10:44 +0200 Subject: [PATCH 15/20] IncludePstr_include( Pmod_ident) --- analysis/src/CompletionBackEnd.ml | 17 +- analysis/src/CompletionFrontEnd.ml | 1 + analysis/src/Completions.ml | 3 +- .../src/DotPipeCompleteFromCurrentModule.res | 77 ------ .../tests/src/IncludeModuleCompletion.res | 91 +++++++ .../DotPipeCompleteFromCurrentModule.res.txt | 199 -------------- .../expected/IncludeModuleCompletion.res.txt | 243 ++++++++++++++++++ 7 files changed, 353 insertions(+), 278 deletions(-) create mode 100644 tests/analysis_tests/tests/src/IncludeModuleCompletion.res create mode 100644 tests/analysis_tests/tests/src/expected/IncludeModuleCompletion.res.txt diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 512d397ffd..f41a45f06e 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -458,7 +458,22 @@ let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) |> Hashtbl.iter (fun (name, _) (declared : Types.type_expr Declared.t) -> (* We check all the values if their origin is the same as the include path. *) match declared.modulePath with - | SharedTypes.ModulePath.IncludedModule (source, _) -> + | ModulePath.ExportedModule {name = exportedName} + when exportedName = includePath -> + if Utils.checkName name ~prefix ~exact then + if not (Hashtbl.mem localTables.namesUsed name) then ( + Hashtbl.add localTables.namesUsed name (); + localTables.resultRev <- + { + (Completion.create declared.name.txt ~env + ~kind:(Value declared.item)) + with + deprecated = declared.deprecated; + docstring = declared.docstring; + synthetic = true; + } + :: localTables.resultRev) + | ModulePath.IncludedModule (source, _) -> let source_module_path = match Path.flatten source with | `Contains_apply -> "" diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 03b684ebae..fb92e81d19 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -745,6 +745,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor processed := true | Pstr_include {pincl_mod = {pmod_desc = med}} -> ( match med with + | Pmod_ident {txt = lid; loc} | Pmod_apply ({pmod_desc = Pmod_ident {txt = lid; loc}}, _) -> let module_name = Longident.flatten lid |> String.concat "." in scope := !scope |> Scope.addInclude ~name:module_name ~loc diff --git a/analysis/src/Completions.ml b/analysis/src/Completions.ml index a3db6de8e1..c11d51673e 100644 --- a/analysis/src/Completions.ml +++ b/analysis/src/Completions.ml @@ -9,7 +9,8 @@ let getCompletions ~debug ~path ~pos ~currentFile ~forHover = with | None -> None | Some (completable, scope) -> ( - if debug then ( + (* uncomment when debugging *) + if false then ( Printf.printf "\nScope from frontend:\n"; List.iter (fun item -> diff --git a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res index 039dbf9c5f..e80f4fc0a8 100644 --- a/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res +++ b/tests/analysis_tests/tests/src/DotPipeCompleteFromCurrentModule.res @@ -15,80 +15,3 @@ module Y = { let b = (x:t) => 4 } - - -module Types = { - type comp - type context - type vec2 -} - -module PosComp = ( - T: { - type t - }, -) => { - open Types - - @send - external addPos: (context, float, float) => comp = "pos" - - @send - external addPosFromVec2: (context, vec2) => comp = "pos" -} - -module SpriteComp = ( - T: { - type t - } -) - => { - open Types - - @send - external addSprite: (context, string) => comp = "sprite" - } - -external k: Types.context = "k" - -@send -external add: (Types.context, array) => 't = "add" - -module Wall = { - type t - - include PosComp({ type t = t }) - - let blah = (k: Types.context) => "" - - let make = () => { - [ - // k. - // ^com - - // add - // ^com - ] - } - - let makeWith = (x) => { - k->add([ - k->addPos(1.0, 2.0), - // addP - // ^com - ]) - } - - module Poster = { - type t - - include SpriteComp({ type t = t }) - - let make = () => { - [ - // k. - // ^com - ] - } - } -} \ No newline at end of file diff --git a/tests/analysis_tests/tests/src/IncludeModuleCompletion.res b/tests/analysis_tests/tests/src/IncludeModuleCompletion.res new file mode 100644 index 0000000000..081fe0cd81 --- /dev/null +++ b/tests/analysis_tests/tests/src/IncludeModuleCompletion.res @@ -0,0 +1,91 @@ +module Types = { + type comp + + type context + + type vec2 +} + +module PosComp = ( + T: { + type t + }, +) => { + open Types + + @send + external addPos: (context, float, float) => comp = "pos" + + @send + external addPosFromVec2: (context, vec2) => comp = "pos" +} + +module SpriteComp = ( + T: { + type t + }, +) => { + open Types + + @send + external addSprite: (context, string) => comp = "sprite" +} + +external k: Types.context = "k" + +@send +external add: (Types.context, array) => 't = "add" + +module Wall = { + type t + + include PosComp({type t = t}) + + let blah = (k: Types.context) => "" + + let make = () => { + [ + // k. + // ^com + // add + // ^com + ] + } + + let makeWith = x => { + k->add([ + k->addPos(1.0, 2.0), + + // addP + // ^com + ]) + } + + module Poster = { + type t + + include SpriteComp({type t = t}) + + let make = () => { + [ + // k. + // ^com + ] + } + } +} + +module M = { + let lex = (a: int) => "foo" +} + +module N = { + include M + + let a = 4 + // let o = a.l + // ^com + + // let _ = l + // ^com +} diff --git a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt index 786330a6cf..70f6773fec 100644 --- a/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt +++ b/tests/analysis_tests/tests/src/expected/DotPipeCompleteFromCurrentModule.res.txt @@ -30,202 +30,3 @@ Path }] }] -Complete src/DotPipeCompleteFromCurrentModule.res 65:17 -posCursor:[65:17] posNoWhite:[65:16] Found expr:[63:15->71:5] -posCursor:[65:17] posNoWhite:[65:16] Found expr:[64:8->70:9] -posCursor:[65:17] posNoWhite:[65:16] Found expr:[65:15->65:17] -Pexp_field [65:15->65:16] _:[70:8->65:17] -Completable: Cpath Value[k]."" -Package opens Stdlib.place holder Pervasives.JsxModules.place holder -Resolved opens 1 Stdlib -ContextPath Value[k]."" -ContextPath Value[k] -Path k -ContextPath Value[k]-> -ContextPath Value[k] -Path k -CPPipe pathFromEnv:Types found:true -Path Types. -Path -[{ - "label": "->blah", - "kind": 12, - "tags": [], - "detail": "Types.context => string", - "documentation": null, - "sortText": "blah", - "insertText": "->blah", - "additionalTextEdits": [{ - "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, - "newText": "" - }] - }, { - "label": "->add", - "kind": 12, - "tags": [], - "detail": "(Types.context, array) => 't", - "documentation": null, - "sortText": "add", - "insertText": "->add", - "additionalTextEdits": [{ - "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, - "newText": "" - }] - }, { - "label": "->addPosFromVec2", - "kind": 12, - "tags": [], - "detail": "(Types.context, Types.vec2) => Types.comp", - "documentation": null, - "sortText": "addPosFromVec2", - "insertText": "->addPosFromVec2", - "additionalTextEdits": [{ - "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, - "newText": "" - }] - }, { - "label": "->addPos", - "kind": 12, - "tags": [], - "detail": "(Types.context, float, float) => Types.comp", - "documentation": null, - "sortText": "addPos", - "insertText": "->addPos", - "additionalTextEdits": [{ - "range": {"start": {"line": 65, "character": 16}, "end": {"line": 65, "character": 17}}, - "newText": "" - }] - }] - -Complete src/DotPipeCompleteFromCurrentModule.res 68:18 -posCursor:[68:18] posNoWhite:[68:17] Found expr:[63:15->71:5] -posCursor:[68:18] posNoWhite:[68:17] Found expr:[64:8->70:9] -posCursor:[68:18] posNoWhite:[68:17] Found expr:[68:15->68:18] -Pexp_ident add:[68:15->68:18] -Completable: Cpath Value[add] -Package opens Stdlib.place holder Pervasives.JsxModules.place holder -Resolved opens 1 Stdlib -ContextPath Value[add] -Path add -[{ - "label": "add", - "kind": 12, - "tags": [], - "detail": "(Types.context, array) => 't", - "documentation": null - }, { - "label": "addPosFromVec2", - "kind": 12, - "tags": [], - "detail": "(Types.context, Types.vec2) => Types.comp", - "documentation": null - }, { - "label": "addPos", - "kind": 12, - "tags": [], - "detail": "(Types.context, float, float) => Types.comp", - "documentation": null - }] - -Complete src/DotPipeCompleteFromCurrentModule.res 76:19 -posCursor:[76:19] posNoWhite:[76:18] Found expr:[73:19->79:5] -posCursor:[76:19] posNoWhite:[76:18] Found expr:[74:8->78:10] -Completable: Cexpression CArgument Value[add]($1)=addP->array -Package opens Stdlib.place holder Pervasives.JsxModules.place holder -Resolved opens 1 Stdlib -ContextPath CArgument Value[add]($1) -ContextPath Value[add] -Path add -[{ - "label": "addPosFromVec2", - "kind": 12, - "tags": [], - "detail": "(Types.context, Types.vec2) => Types.comp", - "documentation": null - }, { - "label": "addPos", - "kind": 12, - "tags": [], - "detail": "(Types.context, float, float) => Types.comp", - "documentation": null - }] - -Complete src/DotPipeCompleteFromCurrentModule.res 88:21 -posCursor:[88:21] posNoWhite:[88:20] Found expr:[86:19->91:9] -posCursor:[88:21] posNoWhite:[88:20] Found expr:[87:12->90:13] -posCursor:[88:21] posNoWhite:[88:20] Found expr:[88:19->88:21] -Pexp_field [88:19->88:20] _:[90:12->88:21] -Completable: Cpath Value[k]."" -Package opens Stdlib.place holder Pervasives.JsxModules.place holder -Resolved opens 1 Stdlib -ContextPath Value[k]."" -ContextPath Value[k] -Path k -ContextPath Value[k]-> -ContextPath Value[k] -Path k -CPPipe pathFromEnv:Types found:true -Path Types. -Path -[{ - "label": "->blah", - "kind": 12, - "tags": [], - "detail": "Types.context => string", - "documentation": null, - "sortText": "blah", - "insertText": "->blah", - "additionalTextEdits": [{ - "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, - "newText": "" - }] - }, { - "label": "->add", - "kind": 12, - "tags": [], - "detail": "(Types.context, array) => 't", - "documentation": null, - "sortText": "add", - "insertText": "->add", - "additionalTextEdits": [{ - "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, - "newText": "" - }] - }, { - "label": "->addSprite", - "kind": 12, - "tags": [], - "detail": "(Types.context, string) => Types.comp", - "documentation": null, - "sortText": "addSprite", - "insertText": "->addSprite", - "additionalTextEdits": [{ - "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, - "newText": "" - }] - }, { - "label": "->addPosFromVec2", - "kind": 12, - "tags": [], - "detail": "(Types.context, Types.vec2) => Types.comp", - "documentation": null, - "sortText": "addPosFromVec2", - "insertText": "->addPosFromVec2", - "additionalTextEdits": [{ - "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, - "newText": "" - }] - }, { - "label": "->addPos", - "kind": 12, - "tags": [], - "detail": "(Types.context, float, float) => Types.comp", - "documentation": null, - "sortText": "addPos", - "insertText": "->addPos", - "additionalTextEdits": [{ - "range": {"start": {"line": 88, "character": 20}, "end": {"line": 88, "character": 21}}, - "newText": "" - }] - }] - diff --git a/tests/analysis_tests/tests/src/expected/IncludeModuleCompletion.res.txt b/tests/analysis_tests/tests/src/expected/IncludeModuleCompletion.res.txt new file mode 100644 index 0000000000..57535091fe --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/IncludeModuleCompletion.res.txt @@ -0,0 +1,243 @@ +Complete src/IncludeModuleCompletion.res 47:11 +posCursor:[47:11] posNoWhite:[47:10] Found expr:[45:13->52:3] +posCursor:[47:11] posNoWhite:[47:10] Found expr:[46:4->51:5] +posCursor:[47:11] posNoWhite:[47:10] Found expr:[47:9->47:11] +Pexp_field [47:9->47:10] _:[51:4->47:11] +Completable: Cpath Value[k]."" +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[k]."" +ContextPath Value[k] +Path k +ContextPath Value[k]-> +ContextPath Value[k] +Path k +CPPipe pathFromEnv:Types found:true +Path Types. +Path +[{ + "label": "->blah", + "kind": 12, + "tags": [], + "detail": "Types.context => string", + "documentation": null, + "sortText": "blah", + "insertText": "->blah", + "additionalTextEdits": [{ + "range": {"start": {"line": 47, "character": 10}, "end": {"line": 47, "character": 11}}, + "newText": "" + }] + }, { + "label": "->add", + "kind": 12, + "tags": [], + "detail": "(Types.context, array) => 't", + "documentation": null, + "sortText": "add", + "insertText": "->add", + "additionalTextEdits": [{ + "range": {"start": {"line": 47, "character": 10}, "end": {"line": 47, "character": 11}}, + "newText": "" + }] + }, { + "label": "->addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null, + "sortText": "addPosFromVec2", + "insertText": "->addPosFromVec2", + "additionalTextEdits": [{ + "range": {"start": {"line": 47, "character": 10}, "end": {"line": 47, "character": 11}}, + "newText": "" + }] + }, { + "label": "->addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null, + "sortText": "addPos", + "insertText": "->addPos", + "additionalTextEdits": [{ + "range": {"start": {"line": 47, "character": 10}, "end": {"line": 47, "character": 11}}, + "newText": "" + }] + }] + +Complete src/IncludeModuleCompletion.res 49:12 +posCursor:[49:12] posNoWhite:[49:11] Found expr:[45:13->52:3] +posCursor:[49:12] posNoWhite:[49:11] Found expr:[46:4->51:5] +posCursor:[49:12] posNoWhite:[49:11] Found expr:[49:9->49:12] +Pexp_ident add:[49:9->49:12] +Completable: Cpath Value[add] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[add] +Path add +[{ + "label": "add", + "kind": 12, + "tags": [], + "detail": "(Types.context, array) => 't", + "documentation": null + }, { + "label": "addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null + }, { + "label": "addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null + }] + +Complete src/IncludeModuleCompletion.res 58:13 +posCursor:[58:13] posNoWhite:[58:12] Found expr:[54:17->61:3] +posCursor:[58:13] posNoWhite:[58:12] Found expr:[55:4->60:6] +Completable: Cexpression CArgument Value[add]($1)=addP->array +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath CArgument Value[add]($1) +ContextPath Value[add] +Path add +[{ + "label": "addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null + }, { + "label": "addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null + }] + +Complete src/IncludeModuleCompletion.res 70:13 +posCursor:[70:13] posNoWhite:[70:12] Found expr:[68:15->73:5] +posCursor:[70:13] posNoWhite:[70:12] Found expr:[69:6->72:7] +posCursor:[70:13] posNoWhite:[70:12] Found expr:[70:11->70:13] +Pexp_field [70:11->70:12] _:[72:6->70:13] +Completable: Cpath Value[k]."" +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[k]."" +ContextPath Value[k] +Path k +ContextPath Value[k]-> +ContextPath Value[k] +Path k +CPPipe pathFromEnv:Types found:true +Path Types. +Path +[{ + "label": "->blah", + "kind": 12, + "tags": [], + "detail": "Types.context => string", + "documentation": null, + "sortText": "blah", + "insertText": "->blah", + "additionalTextEdits": [{ + "range": {"start": {"line": 70, "character": 12}, "end": {"line": 70, "character": 13}}, + "newText": "" + }] + }, { + "label": "->add", + "kind": 12, + "tags": [], + "detail": "(Types.context, array) => 't", + "documentation": null, + "sortText": "add", + "insertText": "->add", + "additionalTextEdits": [{ + "range": {"start": {"line": 70, "character": 12}, "end": {"line": 70, "character": 13}}, + "newText": "" + }] + }, { + "label": "->addSprite", + "kind": 12, + "tags": [], + "detail": "(Types.context, string) => Types.comp", + "documentation": null, + "sortText": "addSprite", + "insertText": "->addSprite", + "additionalTextEdits": [{ + "range": {"start": {"line": 70, "character": 12}, "end": {"line": 70, "character": 13}}, + "newText": "" + }] + }, { + "label": "->addPosFromVec2", + "kind": 12, + "tags": [], + "detail": "(Types.context, Types.vec2) => Types.comp", + "documentation": null, + "sortText": "addPosFromVec2", + "insertText": "->addPosFromVec2", + "additionalTextEdits": [{ + "range": {"start": {"line": 70, "character": 12}, "end": {"line": 70, "character": 13}}, + "newText": "" + }] + }, { + "label": "->addPos", + "kind": 12, + "tags": [], + "detail": "(Types.context, float, float) => Types.comp", + "documentation": null, + "sortText": "addPos", + "insertText": "->addPos", + "additionalTextEdits": [{ + "range": {"start": {"line": 70, "character": 12}, "end": {"line": 70, "character": 13}}, + "newText": "" + }] + }] + +Complete src/IncludeModuleCompletion.res 85:16 +posCursor:[85:16] posNoWhite:[85:15] Found expr:[85:13->85:16] +Pexp_field [85:13->85:14] l:[85:15->85:16] +Completable: Cpath Value[a].l +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[a].l +ContextPath Value[a] +Path a +ContextPath Value[a]->l +ContextPath Value[a] +Path a +Path Stdlib.Int.l +Path l +[{ + "label": "->lex", + "kind": 12, + "tags": [], + "detail": "int => string", + "documentation": null, + "sortText": "lex", + "insertText": "->lex", + "additionalTextEdits": [{ + "range": {"start": {"line": 85, "character": 14}, "end": {"line": 85, "character": 15}}, + "newText": "" + }] + }] + +Complete src/IncludeModuleCompletion.res 88:14 +posCursor:[88:14] posNoWhite:[88:13] Found expr:[88:13->88:14] +Pexp_ident l:[88:13->88:14] +Completable: Cpath Value[l] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[l] +Path l +[{ + "label": "lex", + "kind": 12, + "tags": [], + "detail": "int => string", + "documentation": null + }] + From 9f66dd2cfdfae5d601985211b57dd11fcd91dbf6 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 15:22:16 +0200 Subject: [PATCH 16/20] Remove path to_string --- analysis/src/CmtViewer.ml | 2 +- compiler/ml/path.ml | 5 ----- compiler/ml/path.mli | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/analysis/src/CmtViewer.ml b/analysis/src/CmtViewer.ml index 6d2aa77ef5..99e71b9537 100644 --- a/analysis/src/CmtViewer.ml +++ b/analysis/src/CmtViewer.ml @@ -95,7 +95,7 @@ let dump ?filter rescript_json cmt_path = dump_module indent m and dump_module indent (module_ : Module.t) = match module_ with - | Ident path -> Printf.printf "Module (Ident) %s\n" (Path.to_string path) + | Ident path -> Printf.printf "Module (Ident) %s\n" (Path.name path) | Structure structure -> dump_structure indent structure | Constraint (m1, m2) -> dump_module indent m1; diff --git a/compiler/ml/path.ml b/compiler/ml/path.ml index 2fde12cb38..d3c78a3b13 100644 --- a/compiler/ml/path.ml +++ b/compiler/ml/path.ml @@ -104,8 +104,3 @@ let is_constructor_typath p = match constructor_typath p with | Regular _ -> false | _ -> true - -let rec to_string = function - | Pident id -> Ident.name id - | Pdot (p, s, _) -> to_string p ^ "." ^ s - | Papply (p1, p2) -> to_string p1 ^ "(" ^ to_string p2 ^ ")" diff --git a/compiler/ml/path.mli b/compiler/ml/path.mli index 6ae0ed4f99..0c24ae12f3 100644 --- a/compiler/ml/path.mli +++ b/compiler/ml/path.mli @@ -42,4 +42,3 @@ type typath = val constructor_typath : t -> typath val is_constructor_typath : t -> bool -val to_string : t -> string From f782f32f62f2c7bafb678d300242ab13ae1fd578 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 18:10:04 +0200 Subject: [PATCH 17/20] Don't let exported override included --- analysis/src/CompletionBackEnd.ml | 15 --------------- analysis/src/LocalTables.ml | 22 +++++++++++++++++++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index f41a45f06e..d2320e04c4 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -458,21 +458,6 @@ let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) |> Hashtbl.iter (fun (name, _) (declared : Types.type_expr Declared.t) -> (* We check all the values if their origin is the same as the include path. *) match declared.modulePath with - | ModulePath.ExportedModule {name = exportedName} - when exportedName = includePath -> - if Utils.checkName name ~prefix ~exact then - if not (Hashtbl.mem localTables.namesUsed name) then ( - Hashtbl.add localTables.namesUsed name (); - localTables.resultRev <- - { - (Completion.create declared.name.txt ~env - ~kind:(Value declared.item)) - with - deprecated = declared.deprecated; - docstring = declared.docstring; - synthetic = true; - } - :: localTables.resultRev) | ModulePath.IncludedModule (source, _) -> let source_module_path = match Path.flatten source with diff --git a/analysis/src/LocalTables.ml b/analysis/src/LocalTables.ml index 5a5bdb0c2c..47dc7d1752 100644 --- a/analysis/src/LocalTables.ml +++ b/analysis/src/LocalTables.ml @@ -25,9 +25,25 @@ let create () = let populateValues ~env localTables = env.QueryEnv.file.stamps |> Stamps.iterValues (fun _ declared -> - Hashtbl.replace localTables.valueTable - (declared.name.txt, declared.name.loc |> Loc.start) - declared) + match declared.modulePath with + | ModulePath.ExportedModule _ -> ( + match + Hashtbl.find_opt localTables.valueTable + (declared.name.txt, declared.name.loc |> Loc.start) + with + | Some + {modulePath = ModulePath.IncludedModule _; name = existingName} + when declared.name.txt = existingName.txt -> + (* Don't override an included module declared item with an Exported one *) + () + | _ -> + Hashtbl.replace localTables.valueTable + (declared.name.txt, declared.name.loc |> Loc.start) + declared) + | _ -> + Hashtbl.replace localTables.valueTable + (declared.name.txt, declared.name.loc |> Loc.start) + declared) let populateConstructors ~env localTables = env.QueryEnv.file.stamps From 4a578bfa4926c0c7619a064859e27455e8868edd Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 18:11:44 +0200 Subject: [PATCH 18/20] Use Path.name --- analysis/src/CompletionBackEnd.ml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index d2320e04c4..dad726bbbd 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -459,12 +459,7 @@ let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) (* We check all the values if their origin is the same as the include path. *) match declared.modulePath with | ModulePath.IncludedModule (source, _) -> - let source_module_path = - match Path.flatten source with - | `Contains_apply -> "" - | `Ok (ident, path) -> ident.name :: path |> String.concat "." - in - + let source_module_path = Path.name source in if String.ends_with ~suffix:includePath source_module_path then (* If this is the case we perform a similar check for the prefix *) if Utils.checkName name ~prefix ~exact then From 0359c7fb4b29f896719fb923a485f694b049ff44 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 18:13:45 +0200 Subject: [PATCH 19/20] Remove duplicate check --- analysis/src/LocalTables.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/analysis/src/LocalTables.ml b/analysis/src/LocalTables.ml index 47dc7d1752..0950e695a5 100644 --- a/analysis/src/LocalTables.ml +++ b/analysis/src/LocalTables.ml @@ -31,9 +31,7 @@ let populateValues ~env localTables = Hashtbl.find_opt localTables.valueTable (declared.name.txt, declared.name.loc |> Loc.start) with - | Some - {modulePath = ModulePath.IncludedModule _; name = existingName} - when declared.name.txt = existingName.txt -> + | Some {modulePath = ModulePath.IncludedModule _} -> (* Don't override an included module declared item with an Exported one *) () | _ -> From 1518f6d35781db24f36894e0d461d00b4050b009 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 29 May 2025 18:29:03 +0200 Subject: [PATCH 20/20] Store included values in separate table --- analysis/src/CompletionBackEnd.ml | 42 +++++++++++++++---------------- analysis/src/LocalTables.ml | 30 +++++++++++----------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index dad726bbbd..f678d9a6c0 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -454,28 +454,26 @@ let processLocalModule name loc ~prefix ~exact ~env let processLocalInclude includePath _loc ~prefix ~exact ~(env : QueryEnv.t) ~(localTables : LocalTables.t) = (* process only values for now *) - localTables.valueTable - |> Hashtbl.iter (fun (name, _) (declared : Types.type_expr Declared.t) -> + localTables.includedValueTable + |> Hashtbl.iter + (fun (name, _) (declared : (string * Types.type_expr) Declared.t) -> (* We check all the values if their origin is the same as the include path. *) - match declared.modulePath with - | ModulePath.IncludedModule (source, _) -> - let source_module_path = Path.name source in - if String.ends_with ~suffix:includePath source_module_path then - (* If this is the case we perform a similar check for the prefix *) - if Utils.checkName name ~prefix ~exact then - if not (Hashtbl.mem localTables.namesUsed name) then ( - Hashtbl.add localTables.namesUsed name (); - localTables.resultRev <- - { - (Completion.create declared.name.txt ~env - ~kind:(Value declared.item)) - with - deprecated = declared.deprecated; - docstring = declared.docstring; - synthetic = true; - } - :: localTables.resultRev) - | _ -> ()) + let source_module_path = fst declared.item in + if String.ends_with ~suffix:includePath source_module_path then + (* If this is the case we perform a similar check for the prefix *) + if Utils.checkName name ~prefix ~exact then + if not (Hashtbl.mem localTables.namesUsed name) then ( + Hashtbl.add localTables.namesUsed name (); + localTables.resultRev <- + { + (Completion.create declared.name.txt ~env + ~kind:(Value (snd declared.item))) + with + deprecated = declared.deprecated; + docstring = declared.docstring; + synthetic = true; + } + :: localTables.resultRev)) let getItemsFromOpens ~opens ~localTables ~prefix ~exact ~completionContext = opens @@ -491,6 +489,7 @@ let getItemsFromOpens ~opens ~localTables ~prefix ~exact ~completionContext = let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) ~env ~prefix ~exact ~opens ~scope = localTables |> LocalTables.populateValues ~env; + localTables |> LocalTables.populateIncludedValues ~env; localTables |> LocalTables.populateConstructors ~env; localTables |> LocalTables.populateModules ~env; @@ -527,6 +526,7 @@ let findLocalCompletionsForValuesAndConstructors ~(localTables : LocalTables.t) let findLocalCompletionsForValues ~(localTables : LocalTables.t) ~env ~prefix ~exact ~opens ~scope = localTables |> LocalTables.populateValues ~env; + localTables |> LocalTables.populateIncludedValues ~env; localTables |> LocalTables.populateModules ~env; scope |> Scope.iterValuesBeforeFirstOpen diff --git a/analysis/src/LocalTables.ml b/analysis/src/LocalTables.ml index 0950e695a5..d690d969d1 100644 --- a/analysis/src/LocalTables.ml +++ b/analysis/src/LocalTables.ml @@ -10,6 +10,7 @@ type t = { modulesTable: Module.t table; typesTable: Type.t table; valueTable: Types.type_expr table; + includedValueTable: (string * Types.type_expr) table; } let create () = @@ -20,28 +21,27 @@ let create () = modulesTable = Hashtbl.create 1; typesTable = Hashtbl.create 1; valueTable = Hashtbl.create 1; + includedValueTable = Hashtbl.create 1; } let populateValues ~env localTables = + env.QueryEnv.file.stamps + |> Stamps.iterValues (fun _ declared -> + Hashtbl.replace localTables.valueTable + (declared.name.txt, declared.name.loc |> Loc.start) + declared) + +let populateIncludedValues ~env localTables = env.QueryEnv.file.stamps |> Stamps.iterValues (fun _ declared -> match declared.modulePath with - | ModulePath.ExportedModule _ -> ( - match - Hashtbl.find_opt localTables.valueTable - (declared.name.txt, declared.name.loc |> Loc.start) - with - | Some {modulePath = ModulePath.IncludedModule _} -> - (* Don't override an included module declared item with an Exported one *) - () - | _ -> - Hashtbl.replace localTables.valueTable - (declared.name.txt, declared.name.loc |> Loc.start) - declared) - | _ -> - Hashtbl.replace localTables.valueTable + | ModulePath.IncludedModule (source, _) -> + let path = Path.name source in + let declared = {declared with item = (path, declared.item)} in + Hashtbl.replace localTables.includedValueTable (declared.name.txt, declared.name.loc |> Loc.start) - declared) + declared + | _ -> ()) let populateConstructors ~env localTables = env.QueryEnv.file.stamps