Browse Source

[display] start working on completion rating

see #9082
Simon Krajewski 5 years ago
parent
commit
3f3b4c511d
2 changed files with 64 additions and 17 deletions
  1. 38 14
      src/context/display/displayException.ml
  2. 26 3
      tests/server/src/DisplayTests.hx

+ 38 - 14
src/context/display/displayException.ml

@@ -56,18 +56,35 @@ let filter_somehow ctx items kind subj =
 		| None -> ""
 		| Some name-> String.lowercase name
 	in
-	let subject_matches s =
-		let rec loop i o =
-			if i < String.length subject then begin
-				let o = String.index_from s o subject.[i] in
-				loop (i + 1) o
+	let determine_cost s =
+		let get_initial_cost o =
+			if o = 0 then
+				0 (* Term starts with subject - perfect *)
+			else begin
+				(* Consider `.` as anchors and determine distance from closest one. Penalize starting distance by factor 2. *)
+				try
+					let last_anchor = String.rindex_from s o '.' in
+					(o - (last_anchor + 1)) * 2
+				with Not_found ->
+					o * 2
 			end
 		in
+		let rec loop i o cost =
+			if i < String.length subject then begin
+				let o' = String.index_from s o subject.[i] in
+				let new_cost = if i = 0 then
+					get_initial_cost o'
+				else
+					(o' - o - 1) * 3 (* Holes are bad, penalize by factor 3. *)
+				in
+				loop (i + 1) o' (cost + new_cost)
+			end else
+				cost + (if o = String.length s - 1 then 0 else 1) (* Slightly penalize for not-exact matches. *)
+		in
 		try
-			loop 0 0;
-			true
+			loop 0 0 0;
 		with Not_found ->
-			false
+			-1
 	in
 	let rec loop items index =
 		match items with
@@ -75,14 +92,14 @@ let filter_somehow ctx items kind subj =
 			()
 		| item :: items ->
 			let name = String.lowercase (get_filter_name item) in
-			if subject_matches name then begin
+			let cost = determine_cost name in
+			if cost >= 0 then begin
 				(* Treat types with lowest priority. The assumption is that they are the only kind
 				   which actually causes the limit to be hit, so we show everything else and then
 				   fill in types. *)
 				match item.ci_kind with
 				| ITType _ ->
-					if DynArray.length ret + DynArray.length acc_types < !max_completion_items then
-						DynArray.add acc_types (item,index);
+					DynArray.add acc_types (item,index,cost);
 				| _ ->
 					DynArray.add ret (CompletionItem.to_json ctx (Some index) item);
 			end;
@@ -91,10 +108,17 @@ let filter_somehow ctx items kind subj =
 			()
 	in
 	loop items 0;
-	DynArray.iter (fun (item,index) ->
-		if DynArray.length ret < !max_completion_items then
+	let acc_types = List.sort (fun (_,_,cost1) (_,_,cost2) ->
+		compare cost1 cost2
+	) (DynArray.to_list acc_types) in
+	let rec loop acc_types = match acc_types with
+		| (item,index,_) :: acc_types when DynArray.length ret < !max_completion_items ->
 			DynArray.add ret (CompletionItem.to_json ctx (Some index) item);
-	) acc_types;
+			loop acc_types
+		| _ ->
+			()
+	in
+	loop acc_types;
 	DynArray.to_list ret,DynArray.length ret
 
 let patch_completion_subject subj =

+ 26 - 3
tests/server/src/DisplayTests.hx

@@ -319,18 +319,41 @@ typedef Foo = {
 		});
 	}
 
-	function testIssue9057() {
+	function testIssue9047() {
 		var transform = Marker.extractMarkers("interface Main { var field(never,s{-1-}et):Int; }");
 		vfs.putContent("Main.hx", transform.source);
 		var args = ["Main", "-js", "main.js"];
-
 		function parseGotoDefintion():GotoDefinitionResult {
 			return haxe.Json.parse(lastResult.stderr).result;
 		}
-
 		runHaxeJson(args, DisplayMethods.FindReferences, {file: new FsPath("Main.hx"), offset: transform.markers[1], contents: transform.source});
 		Assert.same([], parseGotoDefintion().result);
 		runHaxeJson(args, DisplayMethods.FindReferences, {file: new FsPath("Main.hx"), offset: transform.markers[1], contents: transform.source});
 		Assert.same([], parseGotoDefintion().result);
 	}
+
+	function testIssue9082() {
+		var args = ["-cp", ".", "--interp"];
+
+		vfs.putContent("org/Thing.hx", "package org; class Thing {}");
+		vfs.putContent("AThing.hx", "class AThing {}");
+		vfs.putContent("ThingB.hx", "class ThingB {}");
+		runHaxeJson(args, Methods.Initialize, {maxCompletionItems: 2});
+		runHaxeJson(args, ServerMethods.ReadClassPaths, null);
+
+		var transform = Marker.extractMarkers("class C extends Thing{-1-}");
+		vfs.putContent("C.hx", transform.source);
+		runHaxeJson(args, DisplayMethods.Completion, {
+			file: new FsPath("C.hx"),
+			offset: transform.markers[1],
+			wasAutoTriggered: true
+		});
+		var result = parseCompletion();
+		assertHasCompletion(result, function(item) {
+			return switch item {
+				case {kind: Type, args: {path: {pack: ["org"], typeName: "Thing"}}}: true;
+				case _: false;
+			}
+		});
+	}
 }