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.
- checksums.yaml +4 -4
- data/ext/rubydex/graph.c +28 -14
- data/lib/rubydex/bin/rubydex_mcp.exe +0 -0
- data/lib/rubydex/version.rb +1 -1
- data/rbi/rubydex.rbi +4 -4
- data/rust/rubydex/src/main.rs +148 -5
- data/rust/rubydex/src/model/declaration.rs +14 -14
- data/rust/rubydex/src/model/graph.rs +85 -2
- data/rust/rubydex/src/model/ids.rs +30 -0
- data/rust/rubydex/src/model/string_ref.rs +12 -4
- data/rust/rubydex/src/query.rs +83 -15
- data/rust/rubydex/src/resolution.rs +196 -139
- data/rust/rubydex/tests/cli.rs +66 -0
- data/rust/rubydex-mcp/src/server.rs +1 -1
- data/rust/rubydex-sys/src/declaration_api.rs +2 -2
- data/rust/rubydex-sys/src/graph_api.rs +54 -43
- metadata +2 -2
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -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,
|
|
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
|
|
52
|
-
|
|
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
|
-
[
|
|
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
|
|