1
1
open SharedTypes
2
2
3
+ let modulePathFromEnv env = env.QueryEnv. file.moduleName :: List. rev env.pathRev
4
+
5
+ let completionPathFromEnvAndPath env ~path =
6
+ modulePathFromEnv env @ List. rev (Utils. expandPath path)
7
+ |> List. rev |> List. tl |> List. rev
8
+
9
+ let getFullTypeId ~env (path : Path.t ) =
10
+ modulePathFromEnv env @ List. rev (Utils. expandPath path) |> String. concat " ."
11
+
12
+ let fullTypeIdFromDecl ~env ~name ~modulePath =
13
+ env.QueryEnv. file.moduleName :: ModulePath. toPath modulePath name
14
+ |> String. concat " ."
15
+
3
16
let debugLogTypeArgContext {env; typeArgs; typeParams} =
4
17
Printf. sprintf " Type arg context. env: %s, typeArgs: %s, typeParams: %s\n "
5
18
(Debug. debugPrintEnv env)
@@ -259,6 +272,28 @@ let rec extractFunctionType ~env ~package typ =
259
272
in
260
273
loop ~env [] typ
261
274
275
+ let rec extractFunctionTypeWithEnv ~env ~package typ =
276
+ let rec loop ~env acc (t : Types.type_expr ) =
277
+ match t.desc with
278
+ | Tlink t1 | Tsubst t1 | Tpoly (t1 , [] ) -> loop ~env acc t1
279
+ | Tarrow (label , tArg , tRet , _ ) -> loop ~env ((label, tArg) :: acc) tRet
280
+ | Tconstr (Pident {name = "function$" } , [t ; _ ], _ ) ->
281
+ extractFunctionTypeWithEnv ~env ~package t
282
+ | Tconstr (path , typeArgs , _ ) -> (
283
+ match References. digConstructor ~env ~package path with
284
+ | Some
285
+ ( env,
286
+ {
287
+ item = {decl = {type_manifest = Some t1; type_params = typeParams}};
288
+ } ) ->
289
+ let t1 = t1 |> instantiateType ~type Params ~type Args in
290
+ loop ~env acc t1
291
+ | Some _ -> (List. rev acc, t, env)
292
+ | _ -> (List. rev acc, t, env))
293
+ | _ -> (List. rev acc, t, env)
294
+ in
295
+ loop ~env [] typ
296
+
262
297
let maybeSetTypeArgCtx ?typeArgContextFromTypeManifest ~typeParams ~typeArgs env
263
298
=
264
299
match typeArgContextFromTypeManifest with
@@ -567,6 +602,37 @@ let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full
567
602
in
568
603
digToRelevantType ~env ~package t)
569
604
605
+ let rec resolveTypeForPipeCompletion2 ~env ~package ~lhsLoc ~full
606
+ (t : Types.type_expr ) =
607
+ (* If the type we're completing on is a type parameter, we won't be able to
608
+ do completion unless we know what that type parameter is compiled as.
609
+ This attempts to look up the compiled type for that type parameter by
610
+ looking for compiled information at the loc of that expression. *)
611
+ let typFromLoc =
612
+ match t with
613
+ | {Types. desc = Tvar _ } ->
614
+ findReturnTypeOfFunctionAtLoc lhsLoc ~env ~full ~debug: false
615
+ | _ -> None
616
+ in
617
+ match typFromLoc with
618
+ | Some typFromLoc ->
619
+ typFromLoc |> resolveTypeForPipeCompletion2 ~lhs Loc ~env ~package ~full
620
+ | None ->
621
+ let rec digToRelevantType ~env ~package (t : Types.type_expr ) =
622
+ match t.desc with
623
+ | Tlink t1 | Tsubst t1 | Tpoly (t1 , [] ) ->
624
+ digToRelevantType ~env ~package t1
625
+ (* Don't descend into types named "t". Type t is a convention in the ReScript ecosystem. *)
626
+ | Tconstr (path , _ , _ ) when path |> Path. last = " t" -> (env, t)
627
+ | Tconstr (path , _ , _ ) -> (
628
+ match References. digConstructor ~env ~package path with
629
+ | Some (env , {item = {decl = {type_manifest = Some typ } } } ) ->
630
+ digToRelevantType ~env ~package typ
631
+ | _ -> (env, t))
632
+ | _ -> (env, t)
633
+ in
634
+ digToRelevantType ~env ~package t
635
+
570
636
let extractTypeFromResolvedType (typ : Type.t ) ~env ~full =
571
637
match typ.kind with
572
638
| Tuple items -> Some (Tuple (env, items, Ctype. newty (Ttuple items)))
@@ -1074,7 +1140,7 @@ module Codegen = struct
1074
1140
Ast_helper.Exp. case pat (mkFailWithExp () )))
1075
1141
end
1076
1142
1077
- let getPathRelativeToEnv ~debug ~(env : QueryEnv.t ) ~envFromItem path =
1143
+ let getModulePathRelativeToEnv ~debug ~(env : QueryEnv.t ) ~envFromItem path =
1078
1144
match path with
1079
1145
| _ :: pathRev ->
1080
1146
(* type path is relative to the completion environment
@@ -1125,49 +1191,89 @@ let pathToElementProps package =
1125
1191
| Some g -> (g |> String. split_on_char '.' ) @ [" Elements" ; " props" ]
1126
1192
1127
1193
(* * Extracts module to draw extra completions from for the type, if it has been annotated with @editor.completeFrom. *)
1128
- let getExtraModuleToCompleteFromForType ~env ~full (t : Types.type_expr ) =
1194
+ let rec getExtraModuleToCompleteFromForType ~env ~full (t : Types.type_expr ) =
1129
1195
match t |> Shared. digConstructor with
1130
1196
| Some path -> (
1131
1197
match References. digConstructor ~env ~package: full.package path with
1132
1198
| None -> None
1133
- (* | Some (env, {item = {decl = {type_manifest = Some t}}}) ->
1199
+ | Some (env , {item = {decl = {type_manifest = Some t } } } ) ->
1134
1200
getExtraModuleToCompleteFromForType ~env ~full t
1135
-
1136
- This could be commented back in to traverse type aliases.
1137
- Not clear as of now if that makes sense to do or not.
1138
- *)
1139
1201
| Some (_ , {item = {attributes} } ) ->
1140
1202
ProcessAttributes. findEditorCompleteFromAttribute attributes)
1141
1203
| None -> None
1142
1204
1205
+ module StringSet = Set. Make (String )
1206
+
1207
+ let rec getExtraModuleTosCompleteFromForType ~env ~full (t : Types.type_expr ) =
1208
+ let foundModulePaths = ref StringSet. empty in
1209
+ let addToModulePaths attributes =
1210
+ ProcessAttributes. findEditorCompleteFromAttribute2 attributes
1211
+ |> List. iter (fun e ->
1212
+ foundModulePaths :=
1213
+ StringSet. add (e |> String. concat " ." ) ! foundModulePaths)
1214
+ in
1215
+ let rec inner ~env ~full (t : Types.type_expr ) =
1216
+ match t |> Shared. digConstructor with
1217
+ | Some path -> (
1218
+ match References. digConstructor ~env ~package: full.package path with
1219
+ | None -> ()
1220
+ | Some (env , {item = {decl = {type_manifest = Some t } ; attributes} } ) ->
1221
+ addToModulePaths attributes;
1222
+ inner ~env ~full t
1223
+ | Some (_ , {item = {attributes} } ) -> addToModulePaths attributes)
1224
+ | None -> ()
1225
+ in
1226
+ inner ~env ~full t;
1227
+ ! foundModulePaths |> StringSet. elements
1228
+ |> List. map (fun l -> String. split_on_char '.' l)
1229
+
1143
1230
(* * Checks whether the provided type represents a function that takes the provided path
1144
1231
as the first argument (meaning it's pipeable). *)
1145
- let rec fnTakesTypeAsFirstArg ~env ~full ~lastPath t =
1232
+ let rec fnTakesTypeAsFirstArg ~env ~full ~targetTypeId t =
1233
+ (* if Debug.verbose () then
1234
+ Printf.printf "[fnTakesTypeAsFirstArg] start env: %s\n"
1235
+ env.QueryEnv.file.moduleName;*)
1146
1236
match t.Types. desc with
1147
1237
| Tlink t1
1148
1238
| Tsubst t1
1149
1239
| Tpoly (t1, [] )
1150
1240
| Tconstr (Pident {name = "function$" } , [t1 ; _ ], _ ) ->
1151
- fnTakesTypeAsFirstArg ~env ~full ~last Path t1
1241
+ fnTakesTypeAsFirstArg ~env ~full ~target TypeId t1
1152
1242
| Tarrow _ -> (
1153
- match extractFunctionType ~env ~package: full.package t with
1154
- | (Nolabel, t ) :: _ , _ -> (
1155
- let p = pathFromTypeExpr t in
1156
- match p with
1243
+ match extractFunctionTypeWithEnv ~env ~package: full.package t with
1244
+ | (Nolabel, t ) :: _ , _ , env -> (
1245
+ (* if Debug.verbose () then
1246
+ Printf.printf "[fnTakesTypeAsFirstArg] extracted env: %s\n"
1247
+ env.QueryEnv.file.moduleName;*)
1248
+ let mainTypeId =
1249
+ match pathFromTypeExpr t with
1250
+ | None -> None
1251
+ | Some tPath -> Some (getFullTypeId ~env tPath)
1252
+ in
1253
+ (* if Debug.verbose () then
1254
+ Printf.printf "[filterPipeableFunctions]--> targetTypeId:%s = %s\n"
1255
+ targetTypeId
1256
+ (Option.value ~default:"None" mainTypeId);*)
1257
+ match mainTypeId with
1157
1258
| None -> false
1158
- | Some p ->
1159
- (*
1160
- Rules:
1161
- - The path p of the current type in the module we're looking at is relative to the current module.
1162
- - The path we're comparing against, `path`, is assumed to belong to this current module, because we're completing from it.
1163
-
1164
- Therefore, we can safely pluck out just the last part of the `path`, but need to use the entire name of the current type
1165
- we're comparing with.
1166
- *)
1167
- Path. name p = lastPath || Path. name p = " t" )
1259
+ | Some mainTypeId -> mainTypeId = targetTypeId)
1168
1260
| _ -> false )
1169
1261
| _ -> false
1170
1262
1263
+ let getFirstFnUnlabelledArgType ~env ~full t =
1264
+ let labels, _, env =
1265
+ extractFunctionTypeWithEnv ~env ~package: full.package t
1266
+ in
1267
+ let rec findFirstUnlabelledArgType labels =
1268
+ match labels with
1269
+ | (Asttypes. Nolabel, t ) :: _ -> Some t
1270
+ | _ :: rest -> findFirstUnlabelledArgType rest
1271
+ | [] -> None
1272
+ in
1273
+ match findFirstUnlabelledArgType labels with
1274
+ | Some t -> Some (t, env)
1275
+ | _ -> None
1276
+
1171
1277
(* * Turns a completion into a pipe completion. *)
1172
1278
let transformCompletionToPipeCompletion ?(synthetic = false ) ~env ~replaceRange
1173
1279
(completion : Completion.t ) =
@@ -1184,16 +1290,70 @@ let transformCompletionToPipeCompletion ?(synthetic = false) ~env ~replaceRange
1184
1290
synthetic;
1185
1291
}
1186
1292
1293
+ (* * This takes a type expr and the env that type expr was found in, and produces a globally unique
1294
+ id for that specific type. The globally unique id is the full path to the type as seen from the root
1295
+ of the project. Example: type x in module SomeModule in file SomeFile would get the globally
1296
+ unique id `SomeFile.SomeModule.x`.*)
1297
+ let rec findRootTypeId ~full ~env (t : Types.type_expr ) =
1298
+ let debug = Debug. verbose () in
1299
+ (* let debug = false in *)
1300
+ match t.desc with
1301
+ | Tlink t1 | Tsubst t1 | Tpoly (t1 , [] ) -> findRootTypeId ~full ~env t1
1302
+ | Tconstr (Pident {name = "function$" } , [t ; _ ], _ ) ->
1303
+ findRootTypeId ~full ~env t
1304
+ | Tconstr (path , _ , _ ) -> (
1305
+ (* We have a path. Try to dig to its declaration *)
1306
+ if debug then
1307
+ Printf. printf " [findRootTypeId] path %s, dig\n " (Path. name path);
1308
+ match References. digConstructor ~env ~package: full.package path with
1309
+ | Some (env , {item = {decl = {type_manifest = Some t1 } } } ) ->
1310
+ if debug then
1311
+ Printf. printf " [findRootTypeId] dug up type alias at module path %s \n "
1312
+ (modulePathFromEnv env |> String. concat " ." );
1313
+ findRootTypeId ~full ~env t1
1314
+ | Some (env , {item = {name} ; modulePath} ) ->
1315
+ (* if it's a named type, then we know its name will be its module path from the env + its name.*)
1316
+ if debug then
1317
+ Printf. printf
1318
+ " [findRootTypeId] dug up named type at module path %s, from item: %s \n "
1319
+ (modulePathFromEnv env |> String. concat " ." )
1320
+ (ModulePath. toPath modulePath name |> String. concat " ." );
1321
+ Some (fullTypeIdFromDecl ~env ~name ~module Path)
1322
+ | None ->
1323
+ (* If we didn't find anything, then it might be a builtin type. Check it.*)
1324
+ if debug then Printf. printf " [findRootTypeId] dug up non-type alias\n " ;
1325
+ if
1326
+ Predef. builtin_idents
1327
+ |> List. find_opt (fun (_ , i ) -> Ident. same i (Path. head path))
1328
+ |> Option. is_some
1329
+ then
1330
+ Some
1331
+ (if debug then Printf. printf " [findRootTypeId] returning builtin\n " ;
1332
+ Path. name path)
1333
+ else None )
1334
+ | _ -> None
1335
+
1187
1336
(* * Filters out completions that are not pipeable from a list of completions. *)
1188
- let filterPipeableFunctions ~env ~full ?synthetic ?lastPath ?replaceRange
1337
+ let filterPipeableFunctions ~env ~full ?synthetic ?targetTypeId ?replaceRange
1189
1338
completions =
1190
- match lastPath with
1339
+ match targetTypeId with
1191
1340
| None -> completions
1192
- | Some lastPath ->
1341
+ | Some targetTypeId ->
1193
1342
completions
1194
1343
|> List. filter_map (fun (completion : Completion.t ) ->
1195
- match completion.kind with
1196
- | Value t when fnTakesTypeAsFirstArg ~env ~full ~last Path t -> (
1344
+ let thisCompletionItemTypeId =
1345
+ match completion.kind with
1346
+ | Value t -> (
1347
+ match
1348
+ getFirstFnUnlabelledArgType ~full ~env: completion.env t
1349
+ with
1350
+ | None -> None
1351
+ | Some (t , envFromLabelledArg ) ->
1352
+ findRootTypeId ~full ~env: envFromLabelledArg t)
1353
+ | _ -> None
1354
+ in
1355
+ match thisCompletionItemTypeId with
1356
+ | Some mainTypeId when mainTypeId = targetTypeId -> (
1197
1357
match replaceRange with
1198
1358
| None -> Some completion
1199
1359
| Some replaceRange ->
@@ -1216,3 +1376,22 @@ let rec getObjFields (texp : Types.type_expr) =
1216
1376
| Tlink te | Tsubst te | Tpoly (te , [] ) -> te |> getObjFields
1217
1377
| Tvar None -> []
1218
1378
| _ -> []
1379
+
1380
+ let pathToBuiltin path =
1381
+ Predef. builtin_idents
1382
+ |> List. find_opt (fun (_ , i ) -> Ident. same i (Path. head path))
1383
+
1384
+ let completionPathFromMaybeBuiltin path ~package =
1385
+ match pathToBuiltin path with
1386
+ | Some ("array" , _ ) -> Some package.builtInCompletionModules.arrayModulePath
1387
+ | Some ("option" , _ ) -> Some package.builtInCompletionModules.optionModulePath
1388
+ | Some ("string" , _ ) -> Some package.builtInCompletionModules.stringModulePath
1389
+ | Some ("int" , _ ) -> Some package.builtInCompletionModules.intModulePath
1390
+ | Some ("float" , _ ) -> Some package.builtInCompletionModules.floatModulePath
1391
+ | Some ("promise" , _ ) ->
1392
+ Some package.builtInCompletionModules.promiseModulePath
1393
+ | Some ("list" , _ ) -> Some package.builtInCompletionModules.listModulePath
1394
+ | Some ("result" , _ ) -> Some package.builtInCompletionModules.resultModulePath
1395
+ | Some ("dict" , _ ) -> Some [" Dict" ]
1396
+ | Some ("char" , _ ) -> Some [" Char" ]
1397
+ | _ -> None
0 commit comments