rubydex 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,12 +27,25 @@ pub enum MatchMode {
27
27
  Exact,
28
28
  }
29
29
 
30
+ /// Searches all declarations in parallel based on fully qualified names. Accepts multiple queries in case the caller
31
+ /// wants to find multiple patterns without having to re-traverse the graph and also accepts match mode.
32
+ ///
33
+ /// Note: an empty query returns all declarations, so if any are included the rest of the queries will be ignored.
34
+ ///
30
35
  /// # Panics
31
36
  ///
32
37
  /// Will panic if any of the threads panic
33
- pub fn declaration_search(graph: &Graph, query: &str, match_mode: &MatchMode) -> Vec<DeclarationId> {
38
+ pub fn declaration_search(graph: &Graph, queries: &[&str], match_mode: &MatchMode) -> Vec<DeclarationId> {
34
39
  let num_threads = thread::available_parallelism().map_or(4, std::num::NonZero::get);
35
40
  let declarations = graph.declarations();
41
+
42
+ // An empty query matches all declarations as per the LSP specification and is equivalent to fetching all of them
43
+ // directly. Since an empty query matches all, there's no point in checking the other queries or pay the price of
44
+ // spawning threads.
45
+ if queries.iter().any(|q| q.is_empty()) {
46
+ return declarations.keys().copied().collect();
47
+ }
48
+
36
49
  let ids: Vec<DeclarationId> = declarations.keys().copied().collect();
37
50
  let chunk_size = ids.len().div_ceil(num_threads);
38
51
 
@@ -48,16 +61,8 @@ pub fn declaration_search(graph: &Graph, query: &str, match_mode: &MatchMode) ->
48
61
  chunk
49
62
  .iter()
50
63
  .filter(|id| {
51
- let declaration = declarations.get(id).unwrap();
52
- let name = declaration.name();
53
- match match_mode {
54
- MatchMode::Fuzzy => {
55
- // When the query is empty, we return everything as per the LSP specification.
56
- // Otherwise, we compute the match score and return anything with a score greater than zero
57
- query.is_empty() || match_score(query, name) > 0
58
- }
59
- MatchMode::Exact => name.contains(query),
60
- }
64
+ let name = declarations.get(id).unwrap().name();
65
+ queries.iter().any(|query| matches_query(query, name, match_mode))
61
66
  })
62
67
  .copied()
63
68
  .collect::<Vec<_>>()
@@ -69,6 +74,15 @@ pub fn declaration_search(graph: &Graph, query: &str, match_mode: &MatchMode) ->
69
74
  })
70
75
  }
71
76
 
77
+ /// Returns whether a single `query` matches `name` under the given [`MatchMode`].
78
+ #[must_use]
79
+ fn matches_query(query: &str, name: &str, match_mode: &MatchMode) -> bool {
80
+ match match_mode {
81
+ MatchMode::Fuzzy => match_score(query, name) > 0,
82
+ MatchMode::Exact => name.contains(query),
83
+ }
84
+ }
85
+
72
86
  #[must_use]
73
87
  fn match_score(query: &str, target: &str) -> usize {
74
88
  let mut query_chars = query.chars().peekable();
@@ -836,7 +850,7 @@ mod tests {
836
850
  assert_results_eq!($context, $query, &MatchMode::default(), $expected);
837
851
  };
838
852
  ($context:expr, $query:expr, $match_mode:expr, $expected:expr) => {
839
- let actual = declaration_search(&$context.graph(), $query, $match_mode);
853
+ let actual = declaration_search(&$context.graph(), &[$query], $match_mode);
840
854
  assert_eq!(
841
855
  actual,
842
856
  $expected
@@ -945,8 +959,8 @@ mod tests {
945
959
  "
946
960
  });
947
961
  context.resolve();
948
- let exact_results = declaration_search(context.graph(), "", &MatchMode::Exact);
949
- let fuzzy_results = declaration_search(context.graph(), "", &MatchMode::Fuzzy);
962
+ let exact_results = declaration_search(context.graph(), &[""], &MatchMode::Exact);
963
+ let fuzzy_results = declaration_search(context.graph(), &[""], &MatchMode::Fuzzy);
950
964
 
951
965
  assert_eq!(exact_results.len(), fuzzy_results.len());
952
966
  assert_eq!(context.graph().declarations().len(), exact_results.len());
@@ -972,6 +986,51 @@ mod tests {
972
986
  assert_results_eq!(context, "#Is_A?()", ["Foo#is_a_foo?()", "Bar#is_a?()"]);
973
987
  }
974
988
 
989
+ #[test]
990
+ fn multiple_queries_return_union_of_matches() {
991
+ let mut context = GraphTest::new();
992
+ context.index_uri("file:///foo.rb", {
993
+ r"
994
+ class Foo
995
+ def foo_method; end
996
+ def bar_method; end
997
+ def other_method; end
998
+ end
999
+ "
1000
+ });
1001
+ context.resolve();
1002
+
1003
+ let results = declaration_search(context.graph(), &["#foo_method()", "#bar_method()"], &MatchMode::Exact);
1004
+ let mut names: Vec<String> = results
1005
+ .iter()
1006
+ .map(|id| context.graph().declarations().get(id).unwrap().name().to_string())
1007
+ .collect();
1008
+ names.sort();
1009
+
1010
+ assert_eq!(names, ["Foo#bar_method()", "Foo#foo_method()"]);
1011
+ }
1012
+
1013
+ #[test]
1014
+ fn overlapping_queries_do_not_duplicate_results() {
1015
+ let mut context = GraphTest::new();
1016
+ context.index_uri("file:///foo.rb", {
1017
+ r"
1018
+ class Foo
1019
+ def is_a_foo?; end
1020
+ end
1021
+ "
1022
+ });
1023
+ context.resolve();
1024
+
1025
+ let results = declaration_search(context.graph(), &["is_a", "foo?"], &MatchMode::Exact);
1026
+ let matches = results
1027
+ .iter()
1028
+ .filter(|id| context.graph().declarations().get(id).unwrap().name() == "Foo#is_a_foo?()")
1029
+ .count();
1030
+
1031
+ assert_eq!(matches, 1);
1032
+ }
1033
+
975
1034
  fn test_root() -> PathBuf {
976
1035
  let root = if cfg!(windows) { "C:\\" } else { "/" };
977
1036
  PathBuf::from_str(root).unwrap()
@@ -2767,7 +2826,16 @@ mod tests {
2767
2826
  self_decl_id: None,
2768
2827
  nesting_name_id,
2769
2828
  },
2770
- ["Bar#@@bar_cvar"]
2829
+ [
2830
+ "Bar",
2831
+ "Bar#@@bar_cvar",
2832
+ "BasicObject",
2833
+ "Class",
2834
+ "Foo",
2835
+ "Kernel",
2836
+ "Module",
2837
+ "Object"
2838
+ ]
2771
2839
  );
2772
2840
  }
2773
2841