瀏覽代碼

add toplevel completion, resolve positions for position/usage completion (closes #327, closes #569)

Simon Krajewski 11 年之前
父節點
當前提交
8eed508d4a
共有 8 個文件被更改,包括 282 次插入56 次删除
  1. 3 1
      codegen.ml
  2. 1 1
      common.ml
  3. 6 3
      filters.ml
  4. 39 20
      main.ml
  5. 6 2
      std/haxe/macro/Compiler.hx
  6. 1 0
      std/haxe/rtti/CType.hx
  7. 77 8
      typeload.ml
  8. 149 21
      typer.ml

+ 3 - 1
codegen.ml

@@ -925,6 +925,8 @@ let detect_usage com =
 					Type.iter expr e
 				| TLocal v when Meta.has Meta.Usage v.v_meta ->
 					usage := e.epos :: !usage
+				| TVar (v,_) when com.display = DMPosition && Meta.has Meta.Usage v.v_meta ->
+					raise (Typecore.DisplayPosition [e.epos])
 				| _ -> Type.iter expr e
 			in
 			let field cf = match cf.cf_expr with None -> () | Some e -> expr e in
@@ -1710,4 +1712,4 @@ module DeprecationCheck = struct
 			| _ ->
 				()
 		) com.types
-end
+end

+ 1 - 1
common.ml

@@ -104,8 +104,8 @@ type display_mode =
 	| DMNone
 	| DMDefault
 	| DMUsage
-	| DMMetadata
 	| DMPosition
+	| DMToplevel
 
 type context = {
 	(* config *)

+ 6 - 3
filters.ml

@@ -1079,11 +1079,14 @@ let post_process_end() =
 	incr pp_counter
 
 let run com tctx main =
-	if com.display = DMUsage then
-		Codegen.detect_usage com;
+	begin match com.display with
+		| DMUsage | DMPosition ->
+			Codegen.detect_usage com;
+		| _ ->
+			()
+	end;
 	if not (Common.defined com Define.NoDeprecationWarnings) then
 		Codegen.DeprecationCheck.run com;
-
 	(* PASS 1: general expression filters *)
  	let filters = [
  		Codegen.UnificationCallback.run (check_unification com);

+ 39 - 20
main.ml

@@ -1139,17 +1139,24 @@ try
 			| _ ->
 				let file, pos = try ExtString.String.split file_pos "@" with _ -> failwith ("Invalid format : " ^ file_pos) in
 				let file = unquote file in
-				let pos, mode = try ExtString.String.split pos "@" with _ -> pos,"" in
-				let mode = match mode with
-					| "position" -> DMPosition
-					| "usage" -> DMUsage
-					| "metadata" -> DMMetadata
-					| _ -> DMDefault
+				let pos, smode = try ExtString.String.split pos "@" with _ -> pos,"" in
+				let mode = match smode with
+					| "position" ->
+						Common.define com Define.NoCOpt;
+						DMPosition
+					| "usage" ->
+						Common.define com Define.NoCOpt;
+						DMUsage
+					| "toplevel" ->
+						Common.define com Define.NoCOpt;
+						DMToplevel
+					| _ ->
+						DMDefault
 				in
 				let pos = try int_of_string pos with _ -> failwith ("Invalid format : "  ^ pos) in
 				com.display <- mode;
 				Common.display_default := mode;
-				Common.define com Define.Display;
+				Common.define_value com Define.Display (if smode <> "" then smode else "1");
 				Parser.use_doc := true;
 				Parser.resume_display := {
 					Ast.pfile = Common.unique_full_path file;
@@ -1360,7 +1367,12 @@ try
 			"python"
 	) in
 	(* if we are at the last compilation step, allow all packages accesses - in case of macros or opening another project file *)
-	if com.display <> DMNone && not ctx.has_next then com.package_rules <- PMap.foldi (fun p r acc -> match r with Forbidden -> acc | _ -> PMap.add p r acc) com.package_rules PMap.empty;
+	begin match com.display with
+		| DMNone | DMToplevel ->
+			()
+		| _ ->
+			if not ctx.has_next then com.package_rules <- PMap.foldi (fun p r acc -> match r with Forbidden -> acc | _ -> PMap.add p r acc) com.package_rules PMap.empty;
+	end;
 	com.config <- get_config com; (* make sure to adapt all flags changes defined after platform *)
 
 	(* check file extension. In case of wrong commandline, we don't want
@@ -1389,7 +1401,7 @@ try
 		t();
 		if ctx.has_error then raise Abort;
 		begin match com.display with
-			| DMNone | DMUsage ->
+			| DMNone | DMUsage | DMPosition ->
 				()
 			| _ ->
 				if ctx.has_next then raise Abort;
@@ -1522,23 +1534,30 @@ with
 	| Typecore.DisplayPosition pl ->
 		let b = Buffer.create 0 in
 		let error_printer file line = sprintf "%s:%d:" (Common.unique_full_path file) line in
+		Buffer.add_string b "<list>\n";
 		List.iter (fun p ->
 			let epos = Lexer.get_error_pos error_printer p in
-			Buffer.add_string b "<pos>\n";
+			Buffer.add_string b "<pos>";
 			Buffer.add_string b epos;
-			Buffer.add_string b "\n</pos>\n";
+			Buffer.add_string b "</pos>\n";
 		) pl;
+		Buffer.add_string b "</list>";
 		raise (Completion (Buffer.contents b))
-	| Typer.DisplayMetadata m ->
+	| Typer.DisplayToplevel il ->
 		let b = Buffer.create 0 in
-		List.iter (fun (m,el,p) ->
-			Buffer.add_string b ("<meta name=\"" ^ (fst (MetaInfo.to_string m)) ^ "\"");
-			if el = [] then Buffer.add_string b "/>" else begin
-				Buffer.add_string b ">\n";
-				List.iter (fun e -> Buffer.add_string b ((htmlescape (Ast.s_expr e)) ^ "\n")) el;
-				Buffer.add_string b "</meta>\n";
-			end
-		) m;
+		Buffer.add_string b "<il>\n";
+		let ctx = print_context() in
+		let s_type t = htmlescape (s_type ctx t) in
+		List.iter (fun id -> match id with
+			| Typer.ITLocal v -> Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
+			| Typer.ITMember(c,cf) -> Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\">%s</i>\n" (s_type cf.cf_type) cf.cf_name);
+			| Typer.ITStatic(c,cf) -> Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\">%s</i>\n" (s_type cf.cf_type) cf.cf_name);
+			| Typer.ITEnum(en,ef) -> Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\">%s</i>\n" (s_type ef.ef_type) ef.ef_name);
+			| Typer.ITGlobal(mt,s,t) -> Buffer.add_string b (Printf.sprintf "<i k=\"global\" p=\"%s\" t=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (s_type t) s);
+			| Typer.ITType(mt) -> Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (snd (t_infos mt).mt_path));
+			| Typer.ITPackage s -> Buffer.add_string b (Printf.sprintf "<i k=\"package\">%s<i>\n" s)
+		) il;
+		Buffer.add_string b "</il>";
 		raise (Completion (Buffer.contents b))
 	| Parser.TypePath (p,c) ->
 		(match c with

+ 6 - 2
std/haxe/macro/Compiler.hx

@@ -106,11 +106,15 @@ class Compiler {
 		} else {
 			function(c) return Lambda.has(ignore, c);
 		}
+		var displayValue = Context.definedValue("display");
 		if( classPaths == null ) {
 			classPaths = Context.getClassPath();
 			// do not force inclusion when using completion
-			if( Context.defined("display") )
-				return;
+			switch (displayValue) {
+				case null:
+				case "usage":
+				case _: return;
+			}
 			// normalize class path
 			for( i in 0...classPaths.length ) {
 				var cp = StringTools.replace(classPaths[i], "\\", "/");

+ 1 - 0
std/haxe/rtti/CType.hx

@@ -26,6 +26,7 @@ typedef Path = String
 typedef Platforms = List<String>
 
 typedef FunctionArgument = { name : String, opt : Bool, t : CType, ?value:String }
+
 enum CType {
 	CUnknown;
 	CEnum( name : Path, params : List<CType> );

+ 77 - 8
typeload.ml

@@ -1171,6 +1171,67 @@ let type_function_params ctx fd fname p =
 	) fd.f_params;
 	!params
 
+let find_enclosing com e =
+	let display_pos = ref (!Parser.resume_display) in
+	let encloses_display_pos p =
+		if p.pmin <= !display_pos.pmin && p.pmax >= !display_pos.pmax then begin
+			let p = !display_pos in
+			display_pos := { pfile = ""; pmin = -2; pmax = -2 };
+			Some p
+		end else
+			None
+	in
+	let rec loop e = match fst e with
+		| EBlock el ->
+			let p = pos e in
+			(* We want to find the innermost block which contains the display position. *)
+			let el = List.map loop el in
+			let el = match encloses_display_pos p with
+				| None ->
+					el
+				| Some p2 ->
+					let b,el = List.fold_left (fun (b,el) e ->
+						let p = pos e in
+						if b || p.pmax <= p2.pmin then begin
+							(b,e :: el)
+						end else begin
+							let e_d = (EDisplay(e,false)),p in
+							(true,e_d :: el)
+						end
+					) (false,[]) el in
+					let el = if b then
+						el
+					else begin
+						(EDisplay(((EConst(Ident "null")),p),false),p) :: el
+					end in
+					List.rev el
+			in
+			(EBlock el),(pos e)
+		| _ ->
+			Ast.map_expr loop e
+	in
+	loop e
+
+let find_before_pos com e =
+	let display_pos = ref (!Parser.resume_display) in
+	let is_annotated p =
+		if p.pmax = !display_pos.pmin - 1 then begin
+			display_pos := { pfile = ""; pmin = -2; pmax = -2 };
+			true
+		end else
+			false
+	in
+	let rec loop e =
+		if is_annotated (pos e) then
+			(EDisplay(e,false),(pos e))
+		else
+			e
+	in
+	let rec map e =
+		loop (Ast.map_expr map e)
+	in
+	map e
+
 let type_function ctx args ret fmode f do_display p =
 	let locals = save_locals ctx in
 	let fargs = List.map (fun (n,c,t) ->
@@ -1199,15 +1260,23 @@ let type_function ctx args ret fmode f do_display p =
 	ctx.ret <- ret;
 	ctx.opened <- [];
 	let e = match f.f_expr with None -> error "Function body required" p | Some e -> e in
-	let e = if not do_display then type_expr ctx e NoValue else try
-		if Common.defined ctx.com Define.NoCOpt then raise Exit;
-		type_expr ctx (Optimizer.optimize_completion_expr e) NoValue
-	with
-	| Parser.TypePath (_,None) | Exit ->
+	let e = if not do_display then
 		type_expr ctx e NoValue
-	| DisplayTypes [t] when (match follow t with TMono _ -> true | _ -> false) ->
-		 type_expr ctx e NoValue
-	in
+	else begin
+		let e = match ctx.com.display with
+			| DMToplevel -> find_enclosing ctx.com e
+			| DMPosition | DMUsage -> find_before_pos ctx.com e
+			| _ -> e
+		in
+		try
+			if Common.defined ctx.com Define.NoCOpt then raise Exit;
+			type_expr ctx (Optimizer.optimize_completion_expr e) NoValue
+		with
+		| Parser.TypePath (_,None) | Exit ->
+			type_expr ctx e NoValue
+		| DisplayTypes [t] when (match follow t with TMono _ -> true | _ -> false) ->
+			type_expr ctx (if ctx.com.display = DMToplevel then find_enclosing ctx.com e else e) NoValue
+	end in
 	let e = match e.eexpr with
 		| TMeta((Meta.MergeBlock,_,_), ({eexpr = TBlock el} as e1)) -> e1
 		| _ -> e

+ 149 - 21
typer.ml

@@ -37,8 +37,18 @@ type access_mode =
 	| MSet
 	| MCall
 
+type identifier_type =
+	| ITLocal of tvar
+	| ITMember of tclass * tclass_field
+	| ITStatic of tclass * tclass_field
+	| ITEnum of tenum * tenum_field
+	| ITGlobal of module_type * string * t
+	| ITType of module_type
+	| ITPackage of string
+
 exception DisplayFields of (string * t * documentation) list
-exception DisplayMetadata of metadata_entry list
+exception DisplayToplevel of identifier_type list
+
 exception WithTypeError of unify_error list * pos
 
 type access_kind =
@@ -350,6 +360,137 @@ let parse_expr_string ctx s p inl =
 	| EClass { d_data = [{ cff_name = "main"; cff_kind = FFun { f_expr = Some e } }]} -> if inl then e else loop e
 	| _ -> assert false
 
+let collect_toplevel_identifiers ctx =
+	let acc = DynArray.create () in
+
+	(* locals *)
+	PMap.iter (fun _ v ->
+		DynArray.add acc (ITLocal v)
+	) ctx.locals;
+
+	(* member vars *)
+	if ctx.curfun <> FunStatic then begin
+		let rec loop c =
+			List.iter (fun cf ->
+				DynArray.add acc (ITMember(ctx.curclass,cf))
+			) c.cl_ordered_fields;
+			match c.cl_super with
+				| None ->
+					()
+				| Some (csup,tl) ->
+					loop csup; (* TODO: type parameters *)
+		in
+		loop ctx.curclass;
+		(* TODO: local using? *)
+	end;
+
+	(* statics *)
+	List.iter (fun cf ->
+		DynArray.add acc (ITStatic(ctx.curclass,cf))
+	) ctx.curclass.cl_ordered_statics;
+
+	(* enum constructors *)
+	let rec enum_ctors t =
+		match t with
+		| TClassDecl _ | TAbstractDecl _ ->
+			()
+		| TTypeDecl t ->
+			begin match follow t.t_type with
+				| TEnum (e,_) -> enum_ctors (TEnumDecl e)
+				| _ -> ()
+			end
+		| TEnumDecl e ->
+			PMap.iter (fun _ ef ->
+				DynArray.add acc (ITEnum(e,ef))
+			) e.e_constrs;
+	in
+	List.iter enum_ctors ctx.m.curmod.m_types;
+	List.iter enum_ctors ctx.m.module_types;
+
+	(* imported globals *)
+	PMap.iter (fun _ (mt,s) ->
+		try
+			let t = match Typeload.resolve_typedef mt with
+				| TClassDecl c -> (PMap.find s c.cl_statics).cf_type
+				| TEnumDecl en -> (PMap.find s en.e_constrs).ef_type
+				| TAbstractDecl {a_impl = Some c} -> (PMap.find s c.cl_statics).cf_type
+				| _ -> raise Not_found
+			in
+			DynArray.add acc (ITGlobal(mt,s,t))
+		with Not_found ->
+			()
+	) ctx.m.module_globals;
+
+	let module_types = ref [] in
+
+	let add_type mt =
+		let path = (t_infos mt).mt_path in
+		if not (List.exists (fun mt2 -> (t_infos mt2).mt_path = path) !module_types) then module_types := mt :: !module_types
+	in
+
+	(* module types *)
+	List.iter add_type ctx.m.curmod.m_types;
+
+	(* module imports *)
+	List.iter add_type ctx.m.module_types;
+
+	(* module using *)
+	List.iter (fun c ->
+		add_type (TClassDecl c)
+	) ctx.m.module_using;
+
+	(* TODO: wildcard packages. How? *)
+
+	(* packages and toplevel types *)
+	let class_paths = ctx.com.class_path in
+	let class_paths = List.filter (fun s -> s <> "") class_paths in
+
+	let packages = ref [] in
+	let add_package pack =
+		try
+			begin match PMap.find pack ctx.com.package_rules with
+				| Forbidden ->
+					()
+				| _ ->
+					raise Not_found
+			end
+		with Not_found ->
+			if not (List.mem pack !packages) then packages := pack :: !packages
+	in
+
+	List.iter (fun dir ->
+		let entries = Sys.readdir dir in
+		Array.iter (fun file ->
+			match file with
+				| "." | ".." ->
+					()
+				| _ when Sys.is_directory (dir ^ file) ->
+					add_package file
+				| _ ->
+					let l = String.length file in
+					if l > 3 && String.sub file (l - 3) 3 = ".hx" then begin
+						try
+							let name = String.sub file 0 (l - 3) in
+							let md = Typeload.load_module ctx ([],name) Ast.null_pos in
+							List.iter (fun mt ->
+								if (t_infos mt).mt_path = md.m_path then add_type mt
+							) md.m_types
+						with _ ->
+							()
+					end
+		) entries;
+	) class_paths;
+
+	List.iter (fun pack ->
+		DynArray.add acc (ITPackage pack)
+	) !packages;
+
+	List.iter (fun mt ->
+		DynArray.add acc (ITType mt)
+	) !module_types;
+
+	raise (DisplayToplevel (DynArray.to_list acc))
+
 (* ---------------------------------------------------------------------- *)
 (* PASS 3 : type expression & check structure *)
 
@@ -2356,7 +2497,8 @@ and type_vars ctx vl p in_block =
 	save();
 
 	match vl with
-	| [v,eo] -> mk (TVar (v,eo)) ctx.t.tvoid p
+	| [v,eo] ->
+		mk (TVar (v,eo)) ctx.t.tvoid p
 	| _ ->
 		let e = mk (TBlock (List.map (fun (v,e) -> (mk (TVar (v,e)) ctx.t.tvoid p)) vl)) ctx.t.tvoid p in
 		mk (TMeta((Meta.MergeBlock,[],p), e)) e.etype e.epos
@@ -3150,7 +3292,7 @@ and type_expr ctx (e,p) (with_type:with_type) =
 		in
 		let texpr = loop t in
 		mk (TCast (type_expr ctx e Value,Some texpr)) t p
-	| EDisplay (e,iscall) when ctx.com.display = DMUsage ->
+	| EDisplay (e,iscall) when (match ctx.com.display with DMUsage | DMPosition -> true | _ -> false) ->
 		let e = try type_expr ctx e Value with Error (Unknown_ident n,_) -> raise (Parser.TypePath ([n],None)) in
 		begin match e.eexpr with
 		| TField(_,fa) ->
@@ -3158,6 +3300,8 @@ and type_expr ctx (e,p) (with_type:with_type) =
 				| None ->
 					()
 				| Some cf ->
+					if ctx.com.display = DMPosition then
+						raise (DisplayPosition [cf.cf_pos]);
 					cf.cf_meta <- (Meta.Usage,[],p) :: cf.cf_meta;
 			end
 		| TLocal v ->
@@ -3166,6 +3310,8 @@ and type_expr ctx (e,p) (with_type:with_type) =
 			()
 		end;
 		e
+	| EDisplay (_) when ctx.com.display = DMToplevel ->
+		collect_toplevel_identifiers ctx;
 	| EDisplay (e,iscall) ->
 		let old = ctx.in_display in
 		let opt_args args ret = TFun(List.map(fun (n,o,t) -> n,true,t) args,ret) in
@@ -3185,26 +3331,8 @@ and type_expr ctx (e,p) (with_type:with_type) =
 				else if field_name fa = "match" then (match follow e1.etype with
 					| TEnum _ as t -> {e1 with etype = tfun [t] ctx.t.tbool }
 					| _ -> e)
-				else if ctx.com.display = DMPosition then (match extract_field fa with
-					| None -> e
-					| Some cf -> raise (Typecore.DisplayPosition [cf.cf_pos]))
-				else if ctx.com.display = DMMetadata then (match fa with
-					| FStatic (c,cf) | FInstance (c,cf) | FClosure(Some c,cf) -> raise (DisplayMetadata (c.cl_meta @ cf.cf_meta))
-					| _ -> e)
 				else
 					e
-			| TTypeExpr mt when ctx.com.display = DMPosition ->
-				raise (DisplayPosition [match mt with
-					| TClassDecl c -> c.cl_pos
-					| TEnumDecl en -> en.e_pos
-					| TTypeDecl t -> t.t_pos
-					| TAbstractDecl a -> a.a_pos])
-			| TTypeExpr mt when ctx.com.display = DMMetadata ->
-				raise (DisplayMetadata (match mt with
-					| TClassDecl c -> c.cl_meta
-					| TEnumDecl en -> en.e_meta
-					| TTypeDecl t -> t.t_meta
-					| TAbstractDecl a -> a.a_meta))
 			| _ ->
 				e
 		in