rubydex 0.3.0 → 0.4.0
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/README.md +45 -7
- data/THIRD_PARTY_LICENSES.html +238 -2
- data/exe/rdx +2 -149
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/diagnostic.c +75 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/graph.c +13 -45
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +398 -16
- data/ext/rubydex/rubydex.c +2 -0
- data/ext/rubydex/utils.c +11 -4
- data/lib/ruby_lsp/rubydex/addon.rb +211 -0
- data/lib/rubydex/cli/command/console.rb +55 -0
- data/lib/rubydex/cli/command/lint/explain.rb +74 -0
- data/lib/rubydex/cli/command/lint.rb +202 -0
- data/lib/rubydex/cli/command/mcp.rb +30 -0
- data/lib/rubydex/cli/command/query.rb +70 -0
- data/lib/rubydex/cli/command/skill.rb +69 -0
- data/lib/rubydex/cli/command.rb +168 -0
- data/lib/rubydex/cli.rb +93 -0
- data/lib/rubydex/config.rb +59 -0
- data/lib/rubydex/diagnostic.rb +12 -3
- data/lib/rubydex/errors.rb +42 -1
- data/lib/rubydex/graph.rb +10 -3
- data/lib/rubydex/linter/custom_rule.rb +97 -0
- data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
- data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
- data/lib/rubydex/linter/rule_loader.rb +36 -0
- data/lib/rubydex/linter/rule_test_case.rb +343 -0
- data/lib/rubydex/linter/runner.rb +56 -0
- data/lib/rubydex/linter.rb +19 -0
- data/lib/rubydex/location.rb +3 -0
- data/lib/rubydex/mcp_server.rb +1 -2
- data/lib/rubydex/related_information.rb +17 -0
- data/lib/rubydex/rule.rb +33 -0
- data/lib/rubydex/severity.rb +70 -0
- data/lib/rubydex/skill.rb +88 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +6 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
- data/rbi/rubydex.rbi +558 -17
- data/rust/Cargo.lock +2 -2
- data/rust/rubydex/Cargo.toml +1 -1
- data/rust/rubydex/benches/graph_memory.rs +3 -5
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +66 -40
- data/rust/rubydex/src/errors.rs +0 -1
- data/rust/rubydex/src/indexing/local_graph.rs +6 -5
- data/rust/rubydex/src/indexing/rbs_indexer.rs +270 -6
- data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -12
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +134 -81
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +7 -4
- data/rust/rubydex/src/model/declaration.rs +302 -219
- data/rust/rubydex/src/model/graph.rs +27 -40
- data/rust/rubydex/src/model/name.rs +58 -17
- data/rust/rubydex/src/operation/ruby_builder.rs +30 -45
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +63 -0
- data/rust/rubydex/src/query/cypher/tests.rs +26 -1
- data/rust/rubydex/src/query/cypher.rs +8 -11
- data/rust/rubydex/src/query.rs +123 -43
- data/rust/rubydex/src/resolution.rs +139 -187
- data/rust/rubydex/src/resolution_tests.rs +247 -19
- data/rust/rubydex/src/test_utils/context.rs +2 -1
- data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
- data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
- data/rust/rubydex/tests/cli.rs +4 -4
- data/rust/rubydex-sys/src/config_api.rs +205 -0
- data/rust/rubydex-sys/src/cypher_api.rs +791 -0
- data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
- data/rust/rubydex-sys/src/graph_api.rs +13 -194
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +2 -6
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +31 -2
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
use crate::diagnostic_api::DiagnosticSeverity;
|
|
2
|
+
use crate::utils;
|
|
3
|
+
use libc::{c_char, c_void};
|
|
4
|
+
use rubydex::config::{Config, Rule};
|
|
5
|
+
use rubydex::errors::Errors;
|
|
6
|
+
use std::ffi::CString;
|
|
7
|
+
use std::path::Path;
|
|
8
|
+
use std::ptr;
|
|
9
|
+
|
|
10
|
+
/// An opaque pointer to a loaded configuration
|
|
11
|
+
pub type ConfigPointer = *mut c_void;
|
|
12
|
+
|
|
13
|
+
/// The result of loading a configuration file, carrying either a parsed configuration or an error message.
|
|
14
|
+
#[repr(C)]
|
|
15
|
+
pub struct CConfigResult {
|
|
16
|
+
/// Non-null on success: a heap-allocated parsed configuration. Free with `rdx_config_free`.
|
|
17
|
+
pub config: ConfigPointer,
|
|
18
|
+
/// Non-null on error; null on success. Caller must free with `free_c_string`.
|
|
19
|
+
pub error: *const c_char,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl CConfigResult {
|
|
23
|
+
fn success(config: Config) -> Self {
|
|
24
|
+
Self {
|
|
25
|
+
config: Box::into_raw(Box::new(config)).cast::<c_void>(),
|
|
26
|
+
error: ptr::null(),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn error(message: &str) -> Self {
|
|
31
|
+
// Parse errors quote raw file content, which may contain NUL bytes that a C string cannot carry.
|
|
32
|
+
let message = message.replace('\0', "\u{FFFD}");
|
|
33
|
+
|
|
34
|
+
Self {
|
|
35
|
+
config: ptr::null_mut(),
|
|
36
|
+
error: utils::cstring_raw(&message),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Loads the configuration of the workspace rooted at `workspace_path`, which is where its `rubydex.toml` is expected
|
|
42
|
+
/// to be. A workspace without a configuration file produces an empty configuration.
|
|
43
|
+
///
|
|
44
|
+
/// # Safety
|
|
45
|
+
///
|
|
46
|
+
/// - `workspace_path` must be a valid, null-terminated string.
|
|
47
|
+
#[unsafe(no_mangle)]
|
|
48
|
+
pub unsafe extern "C" fn rdx_config_load(workspace_path: *const c_char) -> CConfigResult {
|
|
49
|
+
if workspace_path.is_null() {
|
|
50
|
+
return CConfigResult::error("workspace path is required");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let Ok(workspace_path) = (unsafe { utils::convert_char_ptr_to_string(workspace_path) }) else {
|
|
54
|
+
return CConfigResult::error("workspace path is not valid UTF-8");
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
match Config::load(Path::new(&workspace_path)) {
|
|
58
|
+
Ok(config) => CConfigResult::success(config),
|
|
59
|
+
Err(Errors::ConfigError(message) | Errors::FileError(message)) => CConfigResult::error(&message),
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Returns the root directory of the workspace the configuration was loaded for, as a C string. Caller must free with
|
|
64
|
+
/// `free_c_string`.
|
|
65
|
+
///
|
|
66
|
+
/// # Safety
|
|
67
|
+
///
|
|
68
|
+
/// - `config` must be a valid `ConfigPointer` previously returned by `rdx_config_load`.
|
|
69
|
+
#[unsafe(no_mangle)]
|
|
70
|
+
pub unsafe extern "C" fn rdx_config_workspace_path(config: ConfigPointer) -> *const c_char {
|
|
71
|
+
let config = unsafe { &*config.cast::<Config>() };
|
|
72
|
+
|
|
73
|
+
CString::new(utils::interop_path(config.workspace_path()))
|
|
74
|
+
.map_or(ptr::null(), |c_string| c_string.into_raw().cast_const())
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// Frees a configuration through its pointer. Does nothing when given NULL.
|
|
78
|
+
///
|
|
79
|
+
/// # Safety
|
|
80
|
+
///
|
|
81
|
+
/// - `config` must either be NULL or a valid `ConfigPointer` previously returned by `rdx_config_load` and must not
|
|
82
|
+
/// be used afterwards. Any string pointer borrowed from it becomes invalid.
|
|
83
|
+
#[unsafe(no_mangle)]
|
|
84
|
+
pub unsafe extern "C" fn rdx_config_free(config: ConfigPointer) {
|
|
85
|
+
if config.is_null() {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
unsafe {
|
|
90
|
+
let _ = Box::from_raw(config.cast::<Config>());
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Borrowed string bytes exposed while building Ruby configuration values.
|
|
95
|
+
#[repr(C)]
|
|
96
|
+
#[derive(Debug)]
|
|
97
|
+
pub struct CConfigString {
|
|
98
|
+
pub data: *const c_char,
|
|
99
|
+
pub length: usize,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// C-compatible struct representing a single configured linter rule.
|
|
103
|
+
#[repr(C)]
|
|
104
|
+
#[derive(Debug)]
|
|
105
|
+
pub struct CLinterRule {
|
|
106
|
+
pub name: *const c_char,
|
|
107
|
+
pub name_length: usize,
|
|
108
|
+
pub enabled: bool,
|
|
109
|
+
pub exclude_patterns: *const CConfigString,
|
|
110
|
+
pub exclude_patterns_length: usize,
|
|
111
|
+
pub severity: *const DiagnosticSeverity,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
impl From<&Rule> for CLinterRule {
|
|
115
|
+
fn from(rule: &Rule) -> Self {
|
|
116
|
+
let exclude_patterns = rule
|
|
117
|
+
.exclude_patterns()
|
|
118
|
+
.iter()
|
|
119
|
+
.map(|pattern| CConfigString {
|
|
120
|
+
data: pattern.as_ptr().cast::<c_char>(),
|
|
121
|
+
length: pattern.len(),
|
|
122
|
+
})
|
|
123
|
+
.collect::<Box<[CConfigString]>>();
|
|
124
|
+
let exclude_patterns_length = exclude_patterns.len();
|
|
125
|
+
let exclude_patterns = if exclude_patterns.is_empty() {
|
|
126
|
+
ptr::null()
|
|
127
|
+
} else {
|
|
128
|
+
Box::into_raw(exclude_patterns).cast::<CConfigString>().cast_const()
|
|
129
|
+
};
|
|
130
|
+
let severity = rule.severity().map_or(ptr::null(), |severity| {
|
|
131
|
+
Box::into_raw(Box::new(DiagnosticSeverity::from(*severity))).cast_const()
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
Self {
|
|
135
|
+
name: rule.name().as_ptr().cast::<c_char>(),
|
|
136
|
+
name_length: rule.name().len(),
|
|
137
|
+
enabled: rule.enabled(),
|
|
138
|
+
exclude_patterns,
|
|
139
|
+
exclude_patterns_length,
|
|
140
|
+
severity,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// C-compatible array of configured linter rules.
|
|
146
|
+
#[repr(C)]
|
|
147
|
+
pub struct CLinterRuleArray {
|
|
148
|
+
pub items: *mut CLinterRule,
|
|
149
|
+
pub len: usize,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Returns the configured linter rules as an array. Caller must free it with `rdx_config_linter_rules_free`, while the
|
|
153
|
+
/// configuration is still alive, since the rule names are borrowed from it.
|
|
154
|
+
///
|
|
155
|
+
/// # Safety
|
|
156
|
+
///
|
|
157
|
+
/// - `config` must be a valid `ConfigPointer` previously returned by `rdx_config_load`.
|
|
158
|
+
#[unsafe(no_mangle)]
|
|
159
|
+
pub unsafe extern "C" fn rdx_config_linter_rules(config: ConfigPointer) -> CLinterRuleArray {
|
|
160
|
+
let config = unsafe { &*config.cast::<Config>() };
|
|
161
|
+
let rules = config.linter().rules();
|
|
162
|
+
|
|
163
|
+
if rules.is_empty() {
|
|
164
|
+
return CLinterRuleArray {
|
|
165
|
+
items: ptr::null_mut(),
|
|
166
|
+
len: 0,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let items = rules.iter().map(CLinterRule::from).collect::<Box<[CLinterRule]>>();
|
|
171
|
+
|
|
172
|
+
CLinterRuleArray {
|
|
173
|
+
len: items.len(),
|
|
174
|
+
items: Box::into_raw(items).cast::<CLinterRule>(),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/// Frees an array previously returned by `rdx_config_linter_rules`. The rule names it borrowed are left alone, since
|
|
179
|
+
/// they belong to the configuration.
|
|
180
|
+
///
|
|
181
|
+
/// # Safety
|
|
182
|
+
///
|
|
183
|
+
/// - `rules` must have been returned by `rdx_config_linter_rules` and must not be used afterwards.
|
|
184
|
+
#[unsafe(no_mangle)]
|
|
185
|
+
pub unsafe extern "C" fn rdx_config_linter_rules_free(rules: CLinterRuleArray) {
|
|
186
|
+
if rules.items.is_null() {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
unsafe {
|
|
191
|
+
let rules = Box::from_raw(ptr::slice_from_raw_parts_mut(rules.items, rules.len));
|
|
192
|
+
|
|
193
|
+
for rule in &*rules {
|
|
194
|
+
if !rule.exclude_patterns.is_null() {
|
|
195
|
+
let exclude_patterns =
|
|
196
|
+
ptr::slice_from_raw_parts_mut(rule.exclude_patterns.cast_mut(), rule.exclude_patterns_length);
|
|
197
|
+
let _ = Box::from_raw(exclude_patterns);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if !rule.severity.is_null() {
|
|
201
|
+
let _ = Box::from_raw(rule.severity.cast_mut());
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|