Procházet zdrojové kódy

add `@signature` mode

closes #4758
closes #5154
Simon Krajewski před 9 roky
rodič
revize
3227670f6c

+ 53 - 2
src/display/display.ml

@@ -32,6 +32,7 @@ exception DisplayPosition of Ast.pos list
 exception DisplayFields of (string * display_field_kind * documentation) list
 exception DisplayToplevel of IdentifierType.t list
 exception DisplayPackage of string list
+exception DisplaySignature of string
 
 let is_display_file file =
 	file <> "?" && Path.unique_full_path file = (!Parser.resume_display).pfile
@@ -93,7 +94,7 @@ let find_before_pos com e =
 		end else
 			false
 	in
-	let rec loop e =
+	let loop e =
 		if is_annotated (pos e) then
 			(EDisplay(e,false),(pos e))
 		else
@@ -104,6 +105,24 @@ let find_before_pos com e =
 	in
 	map e
 
+let find_display_call e =
+	let found = ref false in
+	let loop e = if !found then e else match fst e with
+		| ECall _ | ENew _ when is_display_position (pos e) ->
+			found := true;
+			(EDisplay(e,true),(pos e))
+		| _ ->
+			e
+	in
+	let rec map e = match fst e with
+		| EDisplay(_,true) ->
+			found := true;
+			e
+		| EDisplay(e1,false) -> map e1
+		| _ -> loop (Ast.map_expr map e)
+	in
+	map e
+
 let display_module_type dm mt p = match dm.dms_kind with
 	| DMPosition -> raise (DisplayPosition [(t_infos mt).mt_pos]);
 	| DMUsage _ ->
@@ -450,6 +469,7 @@ let convert_import_to_something_usable pt path =
 let process_expr com e = match com.display.dms_kind with
 	| DMToplevel -> find_enclosing com e
 	| DMPosition | DMUsage _ | DMType -> find_before_pos com e
+	| DMSignature -> find_display_call e
 	| _ -> e
 
 let add_import_position com p path =
@@ -1153,4 +1173,35 @@ module ToplevelCollector = struct
 		let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
 		let cl = StringError.filter_similar (fun (s,_) r -> r > 0 && r <= (min (String.length s) (String.length i)) / 3) cl in
 		ctx.com.display_information.unresolved_identifiers <- (i,p,cl) :: ctx.com.display_information.unresolved_identifiers
-end
+end
+
+let display_signature tl display_arg =
+	let st = s_type (print_context()) in
+	let s_arg (n,o,t) = Printf.sprintf "%s%s:%s" (if o then "?" else "") n (st t) in
+	let s_fun args ret = Printf.sprintf "(%s):%s" (String.concat ", " (List.map s_arg args)) (st ret) in
+	let siginf = List.map (fun (t,doc) ->
+		let label = match follow t with TFun(args,ret) -> s_fun args ret | _ -> st t in
+		let parameters = match follow t with
+			| TFun(args,_) ->
+				List.map (fun arg ->
+					let label = s_arg arg in
+					JObject [
+						"label",JString label
+					]
+				) args
+			| _ -> []
+		in
+		let js = [
+			"label",JString label;
+			"parameters",JArray parameters;
+		] in
+		JObject (match doc with None -> js | Some s -> ("documentation",JString s) :: js)
+	) tl in
+	let jo = JObject [
+		"signatures",JArray siginf;
+		"activeParameter",JInt display_arg;
+		"activeSignature",JInt 0;
+	] in
+	let b = Buffer.create 0 in
+	write_json (Buffer.add_string b) jo;
+	Buffer.contents b

+ 3 - 1
src/main.ml

@@ -988,6 +988,8 @@ try
 					| "statistics" ->
 						Common.define com Define.NoCOpt;
 						DMStatistics
+					| "signature" ->
+						DMSignature
 					| "" ->
 						DMDefault
 					| _ ->
@@ -1255,7 +1257,7 @@ with
 				complete_type_path_inner ctx p c cur_package is_import
 		in
 		Option.may (fun fields -> raise (Completion (Display.print_fields fields))) fields
-	| Display.ModuleSymbols s | Display.Diagnostics s | Display.Statistics s | Display.Metadata s ->
+	| Display.ModuleSymbols s | Display.Diagnostics s | Display.Statistics s | Display.Metadata s | Display.DisplaySignature s ->
 		raise (Completion s)
 	| Interp.Sys_exit i ->
 		ctx.flush();

+ 1 - 1
src/syntax/ast.ml

@@ -841,7 +841,7 @@ let s_expr e =
 		| ETernary (e1,e2,e3) -> s_expr_inner tabs e1 ^ " ? " ^ s_expr_inner tabs e2 ^ " : " ^ s_expr_inner tabs e3
 		| ECheckType (e,(t,_)) -> "(" ^ s_expr_inner tabs e ^ " : " ^ s_complex_type tabs t ^ ")"
 		| EMeta (m,e) -> s_metadata tabs m ^ " " ^ s_expr_inner tabs e
-		| EDisplay (e1,_) -> Printf.sprintf "#DISPLAY(%s)" (s_expr_inner tabs e1)
+		| EDisplay (e1,iscall) -> Printf.sprintf "#DISPLAY(%s, %b)" (s_expr_inner tabs e1) iscall
 		| EDisplayNew tp -> Printf.sprintf "#DISPLAY_NEW(%s)" (s_complex_type_path tabs tp)
 	and s_expr_list tabs el sep =
 		(String.concat sep (List.map (s_expr_inner tabs) el))

+ 35 - 31
src/syntax/parser.ml

@@ -590,10 +590,13 @@ let semicolon s =
 			let pos = snd (last_token s) in
 			if do_resume() then pos else error Missing_semicolon pos
 
+let encloses_resume p =
+	p.pmin <= !resume_display.pmin && p.pmax >= !resume_display.pmax
+
 let would_skip_resume p1 s =
 	match Stream.npeek 1 s with
 	| [ (_,p2) ] ->
-		is_resuming_file p2.pfile && p1.pmax < !resume_display.pmin && p2.pmin >= !resume_display.pmax
+		is_resuming_file p2.pfile && encloses_resume (punion p1 p2)
 	| _ ->
 		false
 
@@ -1325,11 +1328,7 @@ and expr = parser
 	| [< '(Kwd Throw,p); e = expr >] -> (EThrow e,p)
 	| [< '(Kwd New,p1); t = parse_type_path; s >] ->
 		begin match s with parser
-		| [< '(POpen,p) >] ->
-			if is_resuming p then display (EDisplayNew t,punion p1 p);
-			(match s with parser
-			| [< al = psep Comma expr; '(PClose,p2); s >] -> expr_next (ENew (t,al),punion p1 p2) s
-			| [< >] -> serror())
+		| [< '(POpen,po); e = parse_call_params (fun el p2 -> (ENew(t,el)),punion p1 p2) po >] -> expr_next e s
 		| [< >] ->
 			if do_resume() then (ENew(t,[]),punion p1 (pos t))
 			else serror()
@@ -1407,14 +1406,7 @@ and expr_next e1 = parser
 			match e1 with
 			| (EConst (Int v),p2) when p2.pmax = p.pmin -> expr_next (EConst (Float (v ^ ".")),punion p p2) s
 			| _ -> serror())
-	| [< '(POpen,p1); s >] ->
-		if is_resuming p1 then display (EDisplay (e1,true),p1);
-		(match s with parser
-		| [< '(Binop OpOr,p2) when do_resume() >] ->
-			set_resume p1;
-			display (EDisplay (e1,true),p1) (* help for debug display mode *)
-		| [< params = parse_call_params e1; '(PClose,p2); s >] -> expr_next (ECall (e1,params) , punion (pos e1) p2) s
-		| [< >] -> serror())
+	| [< '(POpen,p1); e = parse_call_params (fun el p2 -> (ECall(e1,el)),punion (pos e1) p2) p1; s >] -> expr_next e s
 	| [< '(BkOpen,_); e2 = expr; '(BkClose,p2); s >] ->
 		expr_next (EArray (e1,e2), punion (pos e1) p2) s
 	| [< '(Binop OpGt,p1); s >] ->
@@ -1491,25 +1483,37 @@ and parse_catches etry catches pmax = parser
 	| [< (catch,pmax) = parse_catch etry; s >] -> parse_catches etry (catch :: catches) pmax s
 	| [< >] -> List.rev catches,pmax
 
-and parse_call_params ec s =
-	let e = (try
+and parse_call_params f p1 s =
+	let make_display_call el p2 =
+		let e = f el p2 in
+		display (EDisplay(e,true),pos e)
+	in
+	if is_resuming p1 then make_display_call [] p1;
+	let rec parse_next_param acc p1 =
+		let e = try
+			expr s
+		with
+		| Stream.Error _ | Stream.Failure as exc ->
+			let p2 = pos (next_token s) in
+			if encloses_resume (punion p1 p2) then make_display_call (List.rev acc) p2
+			else raise exc
+		| Display e ->
+			display (f (List.rev (e :: acc)) (pos e))
+		in
 		match s with parser
-		| [< e = expr >] -> Some e
-		| [< >] -> None
-	with Display e ->
-		display (ECall (ec,[e]),punion (pos ec) (pos e))
-	) in
-	let rec loop acc =
-		try
-			match s with parser
-			| [< '(Comma,_); e = expr >] -> loop (e::acc)
-			| [< >] -> List.rev acc
-		with Display e ->
-			display (ECall (ec,List.rev (e::acc)),punion (pos ec) (pos e))
+		| [< '(PClose,p2) >] -> f (List.rev (e :: acc)) p2
+		| [< '(Comma,p2) >] -> parse_next_param (e :: acc) p2
+		| [< '(Semicolon,p2) >] -> if encloses_resume (punion p1 p2) then make_display_call (List.rev acc) p2 else serror()
+		| [< >] ->
+			let p2 = pos (next_token s) in
+			if encloses_resume (punion p1 p2) then make_display_call (List.rev (e :: acc)) p2 else serror()
 	in
-	match e with
-	| None -> []
-	| Some e -> loop [e]
+	match s with parser
+	| [< '(PClose,p2) >] -> f [] p2
+	| [< '(Binop OpOr,p2) when do_resume() >] ->
+		set_resume p1;
+		make_display_call [] p2
+	| [< >] -> parse_next_param [] p1
 
 and parse_macro_cond allow_op s =
 	match s with parser

+ 3 - 1
src/typing/common.ml

@@ -102,6 +102,7 @@ module DisplayMode = struct
 		| DMModuleSymbols of string option
 		| DMDiagnostics of bool (* true = global, false = only in display file *)
 		| DMStatistics
+		| DMSignature
 
 	type error_policy =
 		| EPIgnore
@@ -156,7 +157,7 @@ module DisplayMode = struct
 		let settings = { default_display_settings with dms_kind = dm } in
 		match dm with
 		| DMNone -> default_compilation_settings
-		| DMDefault | DMPosition | DMResolve _ | DMPackage | DMType -> settings
+		| DMDefault | DMPosition | DMResolve _ | DMPackage | DMType | DMSignature -> settings
 		| DMUsage _ -> { settings with
 				dms_full_typing = true;
 				dms_collect_data = true;
@@ -199,6 +200,7 @@ module DisplayMode = struct
 		| DMModuleSymbols (Some s) -> "workspace-symbols " ^ s
 		| DMDiagnostics b -> (if b then "global " else "") ^ "diagnostics"
 		| DMStatistics -> "statistics"
+		| DMSignature -> "signature"
 end
 
 type compiler_callback = {

+ 5 - 4
src/typing/matcher.ml

@@ -411,9 +411,10 @@ module Pattern = struct
 				v.v_name <- "tmp";
 				let pat = make pctx e1.etype e2 in
 				PatExtractor(v,e1,pat)
-			| EDisplay(e,call) ->
+			| EDisplay(e,iscall) ->
 				let pat = loop e in
-				let _ = Typer.handle_display ctx e call (WithType t) in
+				let _ = if iscall then Typer.handle_signature_display ctx e (WithType t)
+				else Typer.handle_display ctx e (WithType t) in
 				pat
 			| _ ->
 				fail()
@@ -485,8 +486,8 @@ module Case = struct
 		List.iter (fun (v,t) -> v.v_type <- t) old_types;
 		save();
 		if ctx.is_display_file && Display.is_display_position p then begin match eo,eo_ast with
-			| Some e,Some e_ast -> ignore(Typer.display_expr ctx e_ast e false with_type p)
-			| None,None -> ignore(Typer.display_expr ctx (EBlock [],p) (mk (TBlock []) ctx.t.tvoid p) false with_type p)
+			| Some e,Some e_ast -> ignore(Typer.display_expr ctx e_ast e with_type p)
+			| None,None -> ignore(Typer.display_expr ctx (EBlock [],p) (mk (TBlock []) ctx.t.tvoid p) with_type p)
 			| _ -> assert false
 		end;
 		{

+ 102 - 49
src/typing/typer.ml

@@ -3142,7 +3142,7 @@ and type_try ctx e1 catches with_type p =
 		let e = type_expr ctx e_ast with_type in
 		(* If the catch position is the display position it means we get completion on the catch keyword or some
 		   punctuation. Otherwise we wouldn't reach this point. *)
-		if ctx.is_display_file && Display.is_display_position pc then ignore(display_expr ctx e_ast e false with_type pc);
+		if ctx.is_display_file && Display.is_display_position pc then ignore(display_expr ctx e_ast e with_type pc);
 		v.v_type <- t2;
 		locals();
 		if with_type <> NoValue then unify ctx e.etype e1.etype e.epos;
@@ -3466,13 +3466,13 @@ and type_expr ctx (e,p) (with_type:with_type) =
 						mk (TConst TNull) t_dynamic p
 				)
 			) in
-			if display then ignore(handle_display ctx (EConst(Ident i.v_name),i.v_pos) false (WithType i.v_type));
+			if display then ignore(handle_display ctx (EConst(Ident i.v_name),i.v_pos) (WithType i.v_type));
 			let e2 = type_expr ctx e2 NoValue in
 			(try Optimizer.optimize_for_loop_iterator ctx i e1 e2 p with Exit -> mk (TFor (i,e1,e2)) ctx.t.tvoid p)
 		in
 		let e = match Optimizer.optimize_for_loop ctx (i,pi) e1 e2 p with
 			| Some e ->
-				if display then ignore(handle_display ctx (EConst(Ident i),pi) false Value);
+				if display then ignore(handle_display ctx (EConst(Ident i),pi) Value);
 				e
 			| None -> default()
 		in
@@ -3604,7 +3604,8 @@ and type_expr ctx (e,p) (with_type:with_type) =
 		let texpr = loop t in
 		mk (TCast (type_expr ctx e Value,Some texpr)) t p
 	| EDisplay (e,iscall) ->
-		handle_display ctx e iscall with_type
+		if iscall then handle_signature_display ctx e with_type
+		else handle_display ctx e with_type
 	| EDisplayNew t ->
 		let t = Typeload.load_instance ctx t true p in
 		(match follow t with
@@ -3671,7 +3672,7 @@ and get_submodule_fields ctx path =
 	) tl in
 	tl
 
-and handle_display ctx e_ast iscall with_type =
+and handle_display ctx e_ast with_type =
 	let old = ctx.in_display,ctx.in_call_args in
 	ctx.in_display <- true;
 	ctx.in_call_args <- false;
@@ -3685,10 +3686,8 @@ and handle_display ctx e_ast iscall with_type =
 		mk (TConst TNull) t p (* This is "probably" a bind skip, let's just use the expected type *)
 	| _ -> try
 		type_expr ctx e_ast with_type
-	with Error (Unknown_ident n,_) when not iscall ->
+	with Error (Unknown_ident n,_) ->
 		raise (Parser.TypePath ([n],None,false))
-	| Error (Unknown_ident "trace",_) ->
-		raise (Display.DisplaySignatures [(tfun [t_dynamic] ctx.com.basic.tvoid,Some "Print given arguments")])
 	| Error (Type_not_found (path,_),_) as err ->
 		begin try
 			raise (Display.DisplayFields (get_submodule_fields ctx path))
@@ -3703,9 +3702,82 @@ and handle_display ctx e_ast iscall with_type =
 	in
 	ctx.in_display <- fst old;
 	ctx.in_call_args <- snd old;
-	display_expr ctx e_ast e iscall with_type p
+	display_expr ctx e_ast e with_type p
+
+and handle_signature_display ctx e_ast with_type =
+	ctx.in_display <- true;
+	let p = pos e_ast in
+	let find_constructor_types t = match follow t with
+		| TInst (c,tl) | TAbstract({a_impl = Some c},tl) ->
+			let ct,cf = get_constructor ctx c tl p in
+			let tl = (ct,cf.cf_doc) :: List.rev_map (fun cf' -> cf'.cf_type,cf.cf_doc) cf.cf_overloads in
+			tl
+		| _ ->
+			[]
+	in
+	let tl,el,p0 = match fst e_ast with
+		| ECall(e1,el) ->
+			let e1 = try
+				type_expr ctx e1 Value
+			with Error (Unknown_ident "trace",_) ->
+				let e = expr_of_type_path (["haxe";"Log"],"trace") p in
+				type_expr ctx e Value
+			in
+			let tl = match e1.eexpr with
+				| TField(_,fa) ->
+					begin match extract_field fa with
+						| Some cf -> (e1.etype,cf.cf_doc) :: List.rev_map (fun cf' -> cf'.cf_type,cf.cf_doc) cf.cf_overloads
+						| None -> [e1.etype,None]
+					end
+				| TConst TSuper ->
+					find_constructor_types e1.etype
+				| _ -> [e1.etype,None]
+			in
+			tl,el,e1.epos
+		| ENew(tpath,el) ->
+			let t = Typeload.load_instance ctx tpath true p in
+			find_constructor_types t,el,pos tpath
+		| _ -> error "Call expected" p
+	in
+	let rec follow_with_callable (t,doc) = match follow t with
+		| TAbstract(a,tl) when Meta.has Meta.Callable a.a_meta -> follow_with_callable (Abstract.get_underlying_type a tl,doc)
+		| t -> (t,doc)
+	in
+	let tl = List.map follow_with_callable tl in
+	let rec loop i p1 el = match el with
+		| (e,p2) :: el ->
+			if Display.is_display_position (punion p1 p2) then i else loop (i + 1) p2 el
+		| [] ->
+			i
+	in
+	let display_arg = loop 0 p0 el in
+	(* If our display position exceeds the argument number we add a null expression in order to make
+	   unify_call_args error out. *)
+	let el = if display_arg >= List.length el then el @ [EConst (Ident "null"),null_pos] else el in
+	let rec loop acc tl = match tl with
+		| (t,doc) :: tl ->
+			let keep t = match t with
+				| TFun (args,r) ->
+					begin try
+						let _ = unify_call_args' ctx el args r p false false in
+						true
+					with
+					| Error(Call_error (Not_enough_arguments _),_) -> true
+					| _ -> false
+					end
+				| _ -> false
+			in
+			loop (if keep t then (t,doc) :: acc else acc) tl
+		| [] ->
+			acc
+	in
+	let overloads = match loop [] tl with [] -> tl | tl -> tl in
+	if ctx.com.display.dms_kind = DMSignature then
+		raise (Display.DisplaySignature (Display.display_signature overloads display_arg))
+	else
+		raise (Display.DisplaySignatures overloads)
 
-and display_expr ctx e_ast e iscall with_type p =
+and display_expr ctx e_ast e with_type p =
 	let get_super_constructor () = match ctx.curclass.cl_super with
 		| None -> error "Current class does not have a super" p
 		| Some (c,params) ->
@@ -3713,7 +3785,7 @@ and display_expr ctx e_ast e iscall with_type p =
 			f
 	in
 	match ctx.com.display.dms_kind with
-	| DMResolve _ | DMPackage ->
+	| DMResolve _ | DMPackage | DMSignature ->
 		assert false
 	| DMType ->
 		let rec loop e = match e.eexpr with
@@ -3804,19 +3876,15 @@ and display_expr ctx e_ast e iscall with_type p =
 		raise (Display.DisplayToplevel (Display.ToplevelCollector.run ctx false))
 	| DMDefault | DMNone | DMModuleSymbols _ | DMDiagnostics _ | DMStatistics ->
 		let opt_args args ret = TFun(List.map(fun (n,o,t) -> n,true,t) args,ret) in
-		let e,tl_overloads,doc = match e.eexpr with
+		let e = match e.eexpr with
 			| TField (e1,fa) ->
-				let tl,doc = match extract_field fa with
-					| Some cf when iscall -> (List.map (fun cf -> (cf.cf_type,cf.cf_doc)) cf.cf_overloads),cf.cf_doc
-					| _ -> [],None
-				in
 				if field_name fa = "bind" then (match follow e1.etype with
-					| TFun(args,ret) -> {e1 with etype = opt_args args ret},tl,doc
-					| _ -> e,tl,doc)
+					| TFun(args,ret) -> {e1 with etype = opt_args args ret}
+					| _ -> e)
 				else
-					e,tl,doc
+					e
 			| _ ->
-				e,[],None
+				e
 		in
 		let opt_type t =
 			match t with
@@ -3950,35 +4018,20 @@ and display_expr ctx e_ast e iscall with_type p =
 				fields
 		in
 		let fields = PMap.fold (fun f acc -> if Meta.has Meta.NoCompletion f.cf_meta then acc else f :: acc) fields [] in
-		let t = if iscall then
-			let rec loop t = match follow t with
-				| TFun _ -> t
-				| TAbstract(a,tl) when Meta.has Meta.Callable a.a_meta -> loop (Abstract.get_underlying_type a tl)
-				| _ -> t_dynamic
-			in
-			loop e.etype
-		else
-			let get_field acc f =
-				List.fold_left (fun acc f ->
-					let kind = match f.cf_kind with Method _ -> Display.FKMethod f.cf_type | Var _ -> Display.FKVar f.cf_type in
-					if f.cf_public then (f.cf_name,kind,f.cf_doc) :: acc else acc
-				) acc (f :: f.cf_overloads)
-			in
-			let fields = List.fold_left get_field [] fields in
-			let fields = try
-				let sl = string_list_of_expr_path_raise e_ast in
-				fields @ get_submodule_fields ctx (List.tl sl,List.hd sl)
-			with Exit | Not_found ->
-				fields
-			in
-			if fields = [] then
-				e.etype
-			else
-				raise (Display.DisplayFields fields)
+		let get_field acc f =
+			List.fold_left (fun acc f ->
+				let kind = match f.cf_kind with Method _ -> Display.FKMethod f.cf_type | Var _ -> Display.FKVar f.cf_type in
+				if f.cf_public then (f.cf_name,kind,f.cf_doc) :: acc else acc
+			) acc (f :: f.cf_overloads)
 		in
-		(match follow t with
-		| TMono _ | TDynamic _ when ctx.in_macro -> mk (TConst TNull) t p
-		| _ -> raise (Display.DisplaySignatures ((t,doc) :: tl_overloads)))
+		let fields = List.fold_left get_field [] fields in
+		let fields = try
+			let sl = string_list_of_expr_path_raise e_ast in
+			fields @ get_submodule_fields ctx (List.tl sl,List.hd sl)
+		with Exit | Not_found ->
+			fields
+		in
+		raise (Display.DisplayFields fields)
 
 and maybe_type_against_enum ctx f with_type p =
 	try
@@ -4089,7 +4142,7 @@ and type_call ctx e el (with_type:with_type) p =
 		else
 			e
 	| (EDisplay((EConst (Ident "super"),_ as e1),false),_),_ ->
-		handle_display ctx (ECall(e1,el),p) false with_type
+		handle_display ctx (ECall(e1,el),p) with_type
 	| (EConst (Ident "super"),sp) , el ->
 		if ctx.curfun <> FunConstructor then error "Cannot call super constructor outside class constructor" p;
 		let el, t = (match ctx.curclass.cl_super with

+ 1 - 0
tests/display/src/DisplayTestCase.hx

@@ -19,6 +19,7 @@ class DisplayTestCase {
 	inline function position(pos) return ctx.position(pos);
 	inline function usage(pos) return ctx.usage(pos);
 	inline function range(pos1, pos2) return ctx.range(pos1, pos2);
+	inline function signature(pos1) return ctx.signature(pos1);
 
 	function assert(v:Bool) if (!v) throw "assertion failed";
 

+ 4 - 0
tests/display/src/DisplayTestContext.hx

@@ -69,6 +69,10 @@ class DisplayTestContext {
 		return haxe.Json.parse(callHaxe("0@module-symbols"))[0].symbols;
 	}
 
+	public function signature(pos:Position):SignatureHelp {
+		return haxe.Json.parse(callHaxe('$pos@signature'));
+	}
+
 	function callHaxe(displayPart:String):String {
 		var args = [
 			"-cp", "src",

+ 18 - 0
tests/display/src/SignatureHelp.hx

@@ -0,0 +1,18 @@
+// from vshaxe
+
+typedef SignatureHelp = {
+    var signatures:Array<SignatureInformation>;
+    @:optional var activeSignature:Int;
+    @:optional var activeParameter:Int;
+}
+
+typedef SignatureInformation = {
+    var label:String;
+    @:optional var documentation:String;
+    @:optional var parameters:Array<ParameterInformation>;
+}
+
+typedef ParameterInformation = {
+    var label:String;
+    @:optional var documentation:String;
+}

+ 188 - 0
tests/display/src/cases/Signature.hx

@@ -0,0 +1,188 @@
+package cases;
+
+import SignatureHelp;
+
+class Signature extends DisplayTestCase {
+	/**
+	class Some {
+		function main() {
+			test({-1-}"foo"{-2-},{-3-}12{-4-})
+		}
+
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testGoodAst() {
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(1)));
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(2)));
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(3)));
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(4)));
+	}
+
+	/**
+	class Some {
+		function main() {
+			test({-1-}"foo"{-2-}
+		}
+
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testBadAst() {
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(1)));
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(2)));
+	}
+
+	/**
+		class Some {
+			static function someFunc(a:String, b:Float) { }
+
+			static function main() {
+				var a = "foo";
+				var b = 12;
+				someFunc(a, {v: [a, b, {-1-}
+			}
+		}
+	**/
+	function testUglyAst() {
+		sigEq(1, [["a:String", "b:Float"]], signature(pos(1)));
+	}
+
+	/**
+	class Some {
+		function main() {
+			test({-1-}
+		}
+
+		@:overload(function (a:String, b:Bool):Void { })
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testOverloads1() {
+		sigEq(0, [["a:String", "b:Bool"], ["a:String", "b:Int"]], signature(pos(1)));
+	}
+
+	/**
+	class Some {
+		function main() {
+			test("foo",{-1-}
+		}
+
+		@:overload(function (a:String, b:Bool):Void { })
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testOverloads2() {
+		sigEq(1, [["a:String", "b:Bool"], ["a:String", "b:Int"]], signature(pos(1)));
+	}
+
+	/**
+	class Some {
+		function main() {
+			test("foo", 12{-1-}
+		}
+
+		@:overload(function (a:String, b:Bool):Void { })
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testOverloads3() {
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(1)));
+	}
+
+	/**
+	class Some {
+		function main() {
+			test({-1-} {-2-}
+			var x = 12;
+		}
+
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testInsert1() {
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(1)));
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(2)));
+	}
+
+	/**
+	class Some {
+		function main() {
+			test("foo",{-1-} {-2-}
+			var x = 12;
+		}
+
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testInsert2() {
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(1)));
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(2)));
+	}
+
+	/**
+	class SomeBase {
+		function new(a:Bool, b:Float) { }
+	}
+
+	class Some extends SomeBase {
+		function new(a:String, b:Int) {
+			super({-1-});
+			super(true, 1{-2-});
+		}
+
+		static function main() {
+			new Some({-3-}"fo{-4-}o"{-5-},{-6-} {-7-}12{-8-});
+		}
+	}
+	**/
+	function testConstructorGood() {
+		sigEq(0, [["a:Bool", "b:Float"]], signature(pos(1)));
+		sigEq(1, [["a:Bool", "b:Float"]], signature(pos(2)));
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(3)));
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(4)));
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(5)));
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(6)));
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(7)));
+		sigEq(1, [["a:String", "b:Int"]], signature(pos(8)));
+	}
+
+	/**
+	class Some {
+		function new(a:String, b:Int) { }
+
+		static function main() {
+			new Some({-1-}
+		}
+	}
+	**/
+	function testConstructorBad() {
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(1)));
+	}
+
+	/**
+	class Some {
+		static function main() {
+			test(test({-1-}
+		}
+
+		static function test(a:String, b:Int) { }
+	}
+	**/
+	function testNested() {
+		sigEq(0, [["a:String", "b:Int"]], signature(pos(1)));
+	}
+
+	function sigEq(arg:Int, params:Array<Array<String>>, sig:SignatureHelp, ?pos:haxe.PosInfos) {
+		eq(arg, sig.activeParameter, pos);
+		eq(params.length, sig.signatures.length, pos);
+		for (i in 0...params.length) {
+			var sigInf = sig.signatures[i];
+			var args = params[i];
+			eq(sigInf.parameters.length, args.length, pos);
+			for (i in 0...args.length) {
+				eq(sigInf.parameters[i].label, args[i], pos);
+			}
+		}
+	}
+}