From b60b14b0e625d4246b7364ecfbeb15491410fc76 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Tue, 16 Jun 2015 22:24:49 +0300 Subject: [PATCH 1/6] Include data about generic types in rustdoc search index. --- src/librustdoc/html/render.rs | 71 ++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 0080b5e5f223f..18477523905cb 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -253,16 +253,26 @@ struct IndexItem { /// A type used for the search index. struct Type { name: Option, + // true both for Option and T, false otherwise. + generic: bool, + ty_params: Box>, } impl fmt::Display for Type { - /// Formats type as {name: $name}. + /// Formats type as {json}. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Wrapping struct fmt should never call us when self.name is None, // but just to be safe we write `null` in that case. - match self.name { - Some(ref n) => write!(f, "{{\"name\":\"{}\"}}", n), - None => write!(f, "null") + if let Some(ref n) = self.name { + try!(write!(f, "{{\"name\":\"{}\",", n)); + try!(write!(f, "\"generic\":\"{}\",", self.generic)); + let ty_params: Vec = self.ty_params.iter().map(|ref t| { + format!("{}", t) + }).collect(); + // No try, use as return value. + write!(f, "\"ty_params\":[{}]}}", ty_params.connect(",")) + } else { + write!(f, "null") } } } @@ -2566,7 +2576,11 @@ fn get_index_search_type(item: &clean::Item, // Consider `self` an argument as well. if let Some(name) = parent { - inputs.push(Type { name: Some(name.into_ascii_lowercase()) }); + inputs.push(Type { + name: Some(name.into_ascii_lowercase()), + generic: false, + ty_params: Box::new(vec![]), + }); } inputs.extend(&mut decl.inputs.values.iter().map(|arg| { @@ -2581,8 +2595,53 @@ fn get_index_search_type(item: &clean::Item, Some(IndexItemFunctionType { inputs: inputs, output: output }) } +fn is_clean_type_generic(clean_type: &clean::Type) -> bool { + match *clean_type { + clean::ResolvedPath { ref path, is_generic, .. } => { + let segments = &path.segments; + let segment = &segments[segments.len() - 1]; + let has_ty_params = match segment.params { + clean::PathParameters::AngleBracketed { ref types, .. } => !types.is_empty(), + _ => false + }; + is_generic || has_ty_params + } + _ => false + } +} + fn get_index_type(clean_type: &clean::Type) -> Type { - Type { name: get_index_type_name(clean_type).map(|s| s.into_ascii_lowercase()) } + if is_clean_type_generic(clean_type) { + get_generic_index_type(clean_type) + } else { + Type { + name: get_index_type_name(clean_type).map(|s| s.into_ascii_lowercase()), + generic: false, + ty_params: Box::new(vec![]), + } + } +} + +fn get_generic_index_type(clean_type: &clean::Type) -> Type { + let path = match *clean_type { + clean::ResolvedPath { ref path, .. } => path, + _ => unreachable!() + }; + let segments = &path.segments; + let segment = &segments[segments.len() - 1]; + + let ty_params: Vec = match segment.params { + clean::PathParameters::AngleBracketed { ref types, .. } => { + types.iter().map(|t| get_index_type(t)).collect() + } + _ => unreachable!() + }; + + Type { + name: Some(segment.name.clone()), + generic: true, + ty_params: Box::new(ty_params), + } } fn get_index_type_name(clean_type: &clean::Type) -> Option { From f8bbb64e5869e8a3783f6f809f51ee4ac77863aa Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Tue, 16 Jun 2015 23:44:25 +0300 Subject: [PATCH 2/6] Lowercase name for generic as well. --- src/librustdoc/html/render.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 18477523905cb..ba0dfd094d4cf 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2638,7 +2638,7 @@ fn get_generic_index_type(clean_type: &clean::Type) -> Type { }; Type { - name: Some(segment.name.clone()), + name: Some(segment.name.clone().into_ascii_lowercase()), generic: true, ty_params: Box::new(ty_params), } From 2532bc380bda7df096f343c449c212a378e16290 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Wed, 17 Jun 2015 02:14:35 +0300 Subject: [PATCH 3/6] Implement basic generic search in JS. --- src/librustdoc/html/static/main.js | 81 +++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 7f8f40ff08a4d..794b8b284829e 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -224,7 +224,8 @@ for (var i = 0; i < nSearchWords; ++i) { var type = searchIndex[i].type; - if (!type) { + // skip missing types as well as wrong number of args + if (!type || type.inputs.length != inputs.length) { continue; } @@ -239,6 +240,12 @@ output == typeOutput) { results.push({id: i, index: -1, dontValidate: true}); } + // maybe we can find a generic match? + else { + if (genericTypeMatchesQuery(inputs, output, type)) { + results.push({id: i, index: 1, dontValidate: true}); + } + } } } else { // gather matching search results up to a certain maximum @@ -378,6 +385,78 @@ return results; } + /*** + * All combinations of keys using values. + * @return {[[[key, value]]]} [Array of array of pairs] + */ + function generateCombinations(keys, values) { + if (!keys.length) { + return [[]]; + } + var combos = []; + var key = keys[0]; + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + var combo = [[key, value]]; + var next = generateCombinations(keys.slice(1), values.slice(i + 1)); + for (var j = 0; j < next.length; ++j) { + combos.push(combo.concat(next[j])); + } + } + return combos; + } + + /** + * Decide if the generic type (`type`) can be particularized + * to `inputs -> output`. + * + * @param {[string]} inputs [List of types] + * @param {[string]} output [Output type] + * @param {[object]} type [Type from search index] + * @return {[boolean]} [Whether types match] + */ + function genericTypeMatchesQuery(inputs, output, type) { + // Currently only knows about queries such as + // `i32 -> i32` matching against `T -> T`. + var genericInputs = []; + for (var i = 0; i < type.inputs.length; ++i) { + console.log(JSON.stringify(type.inputs[i])); + if (type.inputs[i].generic && type.inputs[i].ty_params.length === 0) { + genericInputs.push(type.inputs[i].name); + } + } + + var possibleMappings = generateCombinations(genericInputs, inputs); + var typeOutput = type.output ? type.output.name : ""; + + console.log(JSON.stringify(possibleMappings)); + // For every possible mapping, try to replace the generics + // accordingly and see if the types match. + for (var i = 0; i < possibleMappings.length; ++i) { + var mapping = possibleMappings[i]; + var newTypeInputs = type.inputs.map(function (input) { + return input.name; + }).sort(); + var newTypeOutput = typeOutput; + for (var j = 0; j < mapping.length; ++j) { + var index = newTypeInputs.indexOf(mapping[j][0]); + newTypeInputs[index] = mapping[j][1]; + if (newTypeOutput === mapping[j][0]) { + newTypeOutput = mapping[j][1]; + } + } + + // Does this new, particularized type, match? + if (inputs.toString() === newTypeInputs.toString() && + output == newTypeOutput) { + return true; + } + } + + // Couldn't find any match till here, sorry. + return false; + } + /** * Validate performs the following boolean logic. For example: * "File::open" will give IF A PARENT EXISTS => ("file" && "open") From 2dda264a503587cdf1de4d84fef5cd96e5f91c4c Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Wed, 17 Jun 2015 02:24:29 +0300 Subject: [PATCH 4/6] Don't use strings for boolean JSON. --- src/librustdoc/html/render.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index ba0dfd094d4cf..b3ff987dd6e32 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -265,7 +265,7 @@ impl fmt::Display for Type { // but just to be safe we write `null` in that case. if let Some(ref n) = self.name { try!(write!(f, "{{\"name\":\"{}\",", n)); - try!(write!(f, "\"generic\":\"{}\",", self.generic)); + try!(write!(f, "\"generic\":{},", self.generic)); let ty_params: Vec = self.ty_params.iter().map(|ref t| { format!("{}", t) }).collect(); From c9764e64e7f85aeb151fb4990a7bcebf5eb432c3 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Wed, 17 Jun 2015 02:24:38 +0300 Subject: [PATCH 5/6] Remove stray console.log calls. --- src/librustdoc/html/static/main.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 794b8b284829e..c19305e4be788 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -420,7 +420,6 @@ // `i32 -> i32` matching against `T -> T`. var genericInputs = []; for (var i = 0; i < type.inputs.length; ++i) { - console.log(JSON.stringify(type.inputs[i])); if (type.inputs[i].generic && type.inputs[i].ty_params.length === 0) { genericInputs.push(type.inputs[i].name); } @@ -429,7 +428,6 @@ var possibleMappings = generateCombinations(genericInputs, inputs); var typeOutput = type.output ? type.output.name : ""; - console.log(JSON.stringify(possibleMappings)); // For every possible mapping, try to replace the generics // accordingly and see if the types match. for (var i = 0; i < possibleMappings.length; ++i) { From 4bc4073aa6b63c8ca52b68228a24e54949b76694 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Sat, 20 Jun 2015 05:22:40 +0300 Subject: [PATCH 6/6] No need for boxed vec. --- src/librustdoc/html/render.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index b3ff987dd6e32..5533d84750a46 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -255,7 +255,7 @@ struct Type { name: Option, // true both for Option and T, false otherwise. generic: bool, - ty_params: Box>, + ty_params: Vec, } impl fmt::Display for Type { @@ -2579,7 +2579,7 @@ fn get_index_search_type(item: &clean::Item, inputs.push(Type { name: Some(name.into_ascii_lowercase()), generic: false, - ty_params: Box::new(vec![]), + ty_params: vec![], }); } @@ -2617,7 +2617,7 @@ fn get_index_type(clean_type: &clean::Type) -> Type { Type { name: get_index_type_name(clean_type).map(|s| s.into_ascii_lowercase()), generic: false, - ty_params: Box::new(vec![]), + ty_params: vec![], } } } @@ -2640,7 +2640,7 @@ fn get_generic_index_type(clean_type: &clean::Type) -> Type { Type { name: Some(segment.name.clone().into_ascii_lowercase()), generic: true, - ty_params: Box::new(ty_params), + ty_params: ty_params, } }