rubydex 0.1.0.beta12-aarch64-linux

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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +23 -0
  3. data/README.md +125 -0
  4. data/THIRD_PARTY_LICENSES.html +4562 -0
  5. data/exe/rdx +47 -0
  6. data/ext/rubydex/declaration.c +453 -0
  7. data/ext/rubydex/declaration.h +23 -0
  8. data/ext/rubydex/definition.c +284 -0
  9. data/ext/rubydex/definition.h +28 -0
  10. data/ext/rubydex/diagnostic.c +6 -0
  11. data/ext/rubydex/diagnostic.h +11 -0
  12. data/ext/rubydex/document.c +97 -0
  13. data/ext/rubydex/document.h +10 -0
  14. data/ext/rubydex/extconf.rb +138 -0
  15. data/ext/rubydex/graph.c +681 -0
  16. data/ext/rubydex/graph.h +10 -0
  17. data/ext/rubydex/handle.h +44 -0
  18. data/ext/rubydex/location.c +22 -0
  19. data/ext/rubydex/location.h +15 -0
  20. data/ext/rubydex/reference.c +123 -0
  21. data/ext/rubydex/reference.h +15 -0
  22. data/ext/rubydex/rubydex.c +22 -0
  23. data/ext/rubydex/utils.c +108 -0
  24. data/ext/rubydex/utils.h +34 -0
  25. data/lib/rubydex/3.2/rubydex.so +0 -0
  26. data/lib/rubydex/3.3/rubydex.so +0 -0
  27. data/lib/rubydex/3.4/rubydex.so +0 -0
  28. data/lib/rubydex/4.0/rubydex.so +0 -0
  29. data/lib/rubydex/comment.rb +17 -0
  30. data/lib/rubydex/diagnostic.rb +21 -0
  31. data/lib/rubydex/failures.rb +15 -0
  32. data/lib/rubydex/graph.rb +98 -0
  33. data/lib/rubydex/keyword.rb +17 -0
  34. data/lib/rubydex/keyword_parameter.rb +13 -0
  35. data/lib/rubydex/librubydex_sys.so +0 -0
  36. data/lib/rubydex/location.rb +90 -0
  37. data/lib/rubydex/mixin.rb +22 -0
  38. data/lib/rubydex/version.rb +5 -0
  39. data/lib/rubydex.rb +23 -0
  40. data/rbi/rubydex.rbi +422 -0
  41. data/rust/Cargo.lock +1851 -0
  42. data/rust/Cargo.toml +29 -0
  43. data/rust/about.hbs +78 -0
  44. data/rust/about.toml +10 -0
  45. data/rust/rubydex/Cargo.toml +42 -0
  46. data/rust/rubydex/src/compile_assertions.rs +13 -0
  47. data/rust/rubydex/src/diagnostic.rs +110 -0
  48. data/rust/rubydex/src/errors.rs +28 -0
  49. data/rust/rubydex/src/indexing/local_graph.rs +224 -0
  50. data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -0
  51. data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -0
  52. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
  53. data/rust/rubydex/src/indexing.rs +210 -0
  54. data/rust/rubydex/src/integrity.rs +279 -0
  55. data/rust/rubydex/src/job_queue.rs +205 -0
  56. data/rust/rubydex/src/lib.rs +17 -0
  57. data/rust/rubydex/src/listing.rs +371 -0
  58. data/rust/rubydex/src/main.rs +160 -0
  59. data/rust/rubydex/src/model/built_in.rs +83 -0
  60. data/rust/rubydex/src/model/comment.rs +24 -0
  61. data/rust/rubydex/src/model/declaration.rs +671 -0
  62. data/rust/rubydex/src/model/definitions.rs +1682 -0
  63. data/rust/rubydex/src/model/document.rs +222 -0
  64. data/rust/rubydex/src/model/encoding.rs +22 -0
  65. data/rust/rubydex/src/model/graph.rs +3754 -0
  66. data/rust/rubydex/src/model/id.rs +110 -0
  67. data/rust/rubydex/src/model/identity_maps.rs +58 -0
  68. data/rust/rubydex/src/model/ids.rs +60 -0
  69. data/rust/rubydex/src/model/keywords.rs +256 -0
  70. data/rust/rubydex/src/model/name.rs +298 -0
  71. data/rust/rubydex/src/model/references.rs +111 -0
  72. data/rust/rubydex/src/model/string_ref.rs +50 -0
  73. data/rust/rubydex/src/model/visibility.rs +41 -0
  74. data/rust/rubydex/src/model.rs +15 -0
  75. data/rust/rubydex/src/offset.rs +147 -0
  76. data/rust/rubydex/src/position.rs +6 -0
  77. data/rust/rubydex/src/query.rs +1841 -0
  78. data/rust/rubydex/src/resolution.rs +6517 -0
  79. data/rust/rubydex/src/stats/memory.rs +71 -0
  80. data/rust/rubydex/src/stats/orphan_report.rs +264 -0
  81. data/rust/rubydex/src/stats/timer.rs +127 -0
  82. data/rust/rubydex/src/stats.rs +11 -0
  83. data/rust/rubydex/src/test_utils/context.rs +226 -0
  84. data/rust/rubydex/src/test_utils/graph_test.rs +730 -0
  85. data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -0
  86. data/rust/rubydex/src/test_utils.rs +52 -0
  87. data/rust/rubydex/src/visualization/dot.rs +192 -0
  88. data/rust/rubydex/src/visualization.rs +6 -0
  89. data/rust/rubydex/tests/cli.rs +185 -0
  90. data/rust/rubydex-mcp/Cargo.toml +28 -0
  91. data/rust/rubydex-mcp/src/main.rs +48 -0
  92. data/rust/rubydex-mcp/src/server.rs +1145 -0
  93. data/rust/rubydex-mcp/src/tools.rs +49 -0
  94. data/rust/rubydex-mcp/tests/mcp.rs +302 -0
  95. data/rust/rubydex-sys/Cargo.toml +20 -0
  96. data/rust/rubydex-sys/build.rs +14 -0
  97. data/rust/rubydex-sys/cbindgen.toml +12 -0
  98. data/rust/rubydex-sys/src/declaration_api.rs +485 -0
  99. data/rust/rubydex-sys/src/definition_api.rs +443 -0
  100. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
  101. data/rust/rubydex-sys/src/document_api.rs +85 -0
  102. data/rust/rubydex-sys/src/graph_api.rs +948 -0
  103. data/rust/rubydex-sys/src/lib.rs +79 -0
  104. data/rust/rubydex-sys/src/location_api.rs +79 -0
  105. data/rust/rubydex-sys/src/name_api.rs +135 -0
  106. data/rust/rubydex-sys/src/reference_api.rs +267 -0
  107. data/rust/rubydex-sys/src/utils.rs +70 -0
  108. data/rust/rustfmt.toml +2 -0
  109. metadata +159 -0
@@ -0,0 +1,298 @@
1
+ use std::fmt::Display;
2
+
3
+ use crate::{
4
+ assert_mem_size,
5
+ model::ids::{DeclarationId, NameId, StringId},
6
+ };
7
+
8
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
9
+ pub enum ParentScope {
10
+ /// There's no parent scope in this reference (e.g.: `Foo`)
11
+ None,
12
+ /// There's an empty parent scope in this reference (e.g.: `::Foo`)
13
+ TopLevel,
14
+ /// There's a parent scope in this reference (e.g.: `Foo::Bar`)
15
+ Some(NameId),
16
+ /// Parent scope representing the class attached to a singleton class context
17
+ ///
18
+ /// `Foo::<Foo>::<<Foo>>`
19
+ /// ^ Attached for <Foo>
20
+ /// ^ Attached for <<Foo>>
21
+ Attached(NameId),
22
+ }
23
+ assert_mem_size!(ParentScope, 16);
24
+
25
+ impl ParentScope {
26
+ pub fn map_or<F, T>(&self, default: T, f: F) -> T
27
+ where
28
+ F: FnOnce(&NameId) -> T,
29
+ {
30
+ match self {
31
+ ParentScope::Some(id) | ParentScope::Attached(id) => f(id),
32
+ _ => default,
33
+ }
34
+ }
35
+
36
+ #[must_use]
37
+ pub fn as_ref(&self) -> Option<&NameId> {
38
+ match self {
39
+ ParentScope::Some(id) | ParentScope::Attached(id) => Some(id),
40
+ _ => None,
41
+ }
42
+ }
43
+
44
+ #[must_use]
45
+ pub fn is_none(&self) -> bool {
46
+ matches!(self, ParentScope::None)
47
+ }
48
+
49
+ #[must_use]
50
+ pub fn is_top_level(&self) -> bool {
51
+ matches!(self, ParentScope::TopLevel)
52
+ }
53
+
54
+ /// # Panics
55
+ ///
56
+ /// Panics if the `ParentScope` is None or `TopLevel`
57
+ #[must_use]
58
+ pub fn expect(&self, message: &str) -> NameId {
59
+ match self {
60
+ ParentScope::Some(id) | ParentScope::Attached(id) => *id,
61
+ _ => panic!("{}", message),
62
+ }
63
+ }
64
+ }
65
+
66
+ impl Display for ParentScope {
67
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68
+ match self {
69
+ ParentScope::None => write!(f, "None"),
70
+ ParentScope::TopLevel => write!(f, "TopLevel"),
71
+ ParentScope::Some(id) => write!(f, "Some({id})"),
72
+ ParentScope::Attached(id) => write!(f, "Attached({id})"),
73
+ }
74
+ }
75
+ }
76
+
77
+ #[derive(Debug, Clone, Eq)]
78
+ pub struct Name {
79
+ /// The unqualified name of the constant
80
+ str: StringId,
81
+ /// The ID of parent scope for this constant. For example:
82
+ ///
83
+ /// ```ruby
84
+ /// Foo::Bar::Baz
85
+ /// # ^ parent scope of Bar::Baz
86
+ /// # ^ parent scope of Baz
87
+ /// ```
88
+ parent_scope: ParentScope,
89
+ /// The ID of the name for the nesting where we found this name. This effectively turns the structure into a linked
90
+ /// list of names to represent the nesting
91
+ nesting: Option<NameId>,
92
+ ref_count: u32,
93
+ }
94
+
95
+ impl PartialEq for Name {
96
+ fn eq(&self, other: &Self) -> bool {
97
+ self.str == other.str && self.parent_scope == other.parent_scope && self.nesting == other.nesting
98
+ }
99
+ }
100
+ assert_mem_size!(Name, 40);
101
+
102
+ impl Name {
103
+ #[must_use]
104
+ pub fn new(str: StringId, parent_scope: ParentScope, nesting: Option<NameId>) -> Self {
105
+ Self {
106
+ str,
107
+ parent_scope,
108
+ nesting,
109
+ ref_count: 1,
110
+ }
111
+ }
112
+
113
+ #[must_use]
114
+ pub fn str(&self) -> &StringId {
115
+ &self.str
116
+ }
117
+
118
+ #[must_use]
119
+ pub fn parent_scope(&self) -> &ParentScope {
120
+ &self.parent_scope
121
+ }
122
+
123
+ #[must_use]
124
+ pub fn nesting(&self) -> &Option<NameId> {
125
+ &self.nesting
126
+ }
127
+
128
+ #[must_use]
129
+ pub fn id(&self) -> NameId {
130
+ NameId::from(&format!(
131
+ "{}{}{}",
132
+ self.str,
133
+ self.parent_scope,
134
+ self.nesting.map_or(String::from("None"), |id| id.to_string())
135
+ ))
136
+ }
137
+ }
138
+
139
+ #[derive(Debug, Clone)]
140
+ pub struct ResolvedName {
141
+ name: Name,
142
+ declaration_id: DeclarationId,
143
+ }
144
+
145
+ impl ResolvedName {
146
+ #[must_use]
147
+ pub fn new(name: Name, declaration_id: DeclarationId) -> Self {
148
+ Self { name, declaration_id }
149
+ }
150
+
151
+ #[must_use]
152
+ pub fn name(&self) -> &Name {
153
+ &self.name
154
+ }
155
+
156
+ #[must_use]
157
+ pub fn declaration_id(&self) -> &DeclarationId {
158
+ &self.declaration_id
159
+ }
160
+
161
+ #[must_use]
162
+ pub fn nesting(&self) -> &Option<NameId> {
163
+ self.name.nesting()
164
+ }
165
+ }
166
+
167
+ /// A usage of a constant name. This could be a constant reference or a definition like a class or module
168
+ #[derive(Debug, Clone)]
169
+ pub enum NameRef {
170
+ /// This name has not yet been resolved. We don't yet know what this name refers to or if it refers to an existing
171
+ /// declaration
172
+ Unresolved(Box<Name>),
173
+ /// This name has been resolved to an existing declaration
174
+ Resolved(Box<ResolvedName>),
175
+ }
176
+
177
+ impl NameRef {
178
+ #[must_use]
179
+ pub fn str(&self) -> &StringId {
180
+ match self {
181
+ NameRef::Unresolved(name) => name.str(),
182
+ NameRef::Resolved(resolved_name) => resolved_name.name.str(),
183
+ }
184
+ }
185
+
186
+ #[must_use]
187
+ pub fn parent_scope(&self) -> &ParentScope {
188
+ match self {
189
+ NameRef::Unresolved(name) => name.parent_scope(),
190
+ NameRef::Resolved(resolved_name) => resolved_name.name.parent_scope(),
191
+ }
192
+ }
193
+
194
+ #[must_use]
195
+ pub fn into_unresolved(self) -> Option<Name> {
196
+ match self {
197
+ NameRef::Unresolved(name) => Some(*name),
198
+ NameRef::Resolved(_) => None,
199
+ }
200
+ }
201
+
202
+ #[must_use]
203
+ pub fn nesting(&self) -> &Option<NameId> {
204
+ match self {
205
+ NameRef::Unresolved(name) => name.nesting(),
206
+ NameRef::Resolved(resolved_name) => resolved_name.name.nesting(),
207
+ }
208
+ }
209
+
210
+ #[must_use]
211
+ pub fn ref_count(&self) -> u32 {
212
+ match self {
213
+ NameRef::Unresolved(name) => name.ref_count,
214
+ NameRef::Resolved(resolved_name) => resolved_name.name.ref_count,
215
+ }
216
+ }
217
+
218
+ /// # Panics
219
+ ///
220
+ /// Panics if we exceed the maximum size of the reference count
221
+ pub fn increment_ref_count(&mut self, count: u32) {
222
+ let ref_count = match self {
223
+ NameRef::Unresolved(name) => &mut name.ref_count,
224
+ NameRef::Resolved(resolved_name) => &mut resolved_name.name.ref_count,
225
+ };
226
+ *ref_count = ref_count
227
+ .checked_add(count)
228
+ .expect("Should not exceed maximum name ref count");
229
+ }
230
+
231
+ #[must_use]
232
+ pub fn decrement_ref_count(&mut self) -> bool {
233
+ match self {
234
+ NameRef::Unresolved(name) => {
235
+ name.ref_count -= 1;
236
+ name.ref_count > 0
237
+ }
238
+ NameRef::Resolved(resolved_name) => {
239
+ resolved_name.name.ref_count -= 1;
240
+ resolved_name.name.ref_count > 0
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ impl PartialEq for NameRef {
247
+ fn eq(&self, other: &Self) -> bool {
248
+ match (self, other) {
249
+ (NameRef::Unresolved(a), NameRef::Unresolved(b)) => a == b,
250
+ (NameRef::Resolved(a), NameRef::Resolved(b)) => a.name == b.name,
251
+ (NameRef::Unresolved(name), NameRef::Resolved(resolved))
252
+ | (NameRef::Resolved(resolved), NameRef::Unresolved(name)) => **name == resolved.name,
253
+ }
254
+ }
255
+ }
256
+
257
+ impl PartialEq<Name> for NameRef {
258
+ fn eq(&self, other: &Name) -> bool {
259
+ match self {
260
+ NameRef::Unresolved(name) => **name == *other,
261
+ NameRef::Resolved(resolved_name) => &resolved_name.name == other,
262
+ }
263
+ }
264
+ }
265
+
266
+ #[cfg(test)]
267
+ mod tests {
268
+ use super::*;
269
+
270
+ #[test]
271
+ fn same_parent_scope_and_nesting() {
272
+ let name_1 = Name::new(StringId::from("Foo"), ParentScope::None, None);
273
+ let name_2 = Name::new(StringId::from("Foo"), ParentScope::None, None);
274
+ assert_eq!(name_1.id(), name_2.id());
275
+
276
+ let name_3 = Name::new(StringId::from("Foo"), ParentScope::Some(name_1.id()), None);
277
+ let name_4 = Name::new(StringId::from("Foo"), ParentScope::Some(name_2.id()), None);
278
+ assert_eq!(name_3.id(), name_4.id());
279
+
280
+ let name_5 = Name::new(StringId::from("Foo"), ParentScope::None, Some(name_1.id()));
281
+ let name_6 = Name::new(StringId::from("Foo"), ParentScope::None, Some(name_2.id()));
282
+ assert_eq!(name_5.id(), name_6.id());
283
+ assert_ne!(name_3.id(), name_5.id());
284
+ assert_ne!(name_4.id(), name_6.id());
285
+
286
+ let name_7 = Name::new(
287
+ StringId::from("Foo"),
288
+ ParentScope::Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
289
+ Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
290
+ );
291
+ let name_8 = Name::new(
292
+ StringId::from("Foo"),
293
+ ParentScope::Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
294
+ Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
295
+ );
296
+ assert_eq!(name_7.id(), name_8.id());
297
+ }
298
+ }
@@ -0,0 +1,111 @@
1
+ use crate::{
2
+ assert_mem_size,
3
+ model::ids::{ConstantReferenceId, MethodReferenceId, NameId, StringId, UriId},
4
+ offset::Offset,
5
+ };
6
+
7
+ /// A reference to a constant
8
+ #[derive(Debug)]
9
+ pub struct ConstantReference {
10
+ /// The name ID of this reference
11
+ name_id: NameId,
12
+ /// The document where we found the reference
13
+ uri_id: UriId,
14
+ /// The offsets inside of the document where we found the reference
15
+ offset: Offset,
16
+ }
17
+ assert_mem_size!(ConstantReference, 24);
18
+
19
+ impl ConstantReference {
20
+ #[must_use]
21
+ pub fn new(name_id: NameId, uri_id: UriId, offset: Offset) -> Self {
22
+ Self {
23
+ name_id,
24
+ uri_id,
25
+ offset,
26
+ }
27
+ }
28
+
29
+ #[must_use]
30
+ pub fn name_id(&self) -> &NameId {
31
+ &self.name_id
32
+ }
33
+
34
+ #[must_use]
35
+ pub fn uri_id(&self) -> UriId {
36
+ self.uri_id
37
+ }
38
+
39
+ #[must_use]
40
+ pub fn offset(&self) -> &Offset {
41
+ &self.offset
42
+ }
43
+
44
+ #[must_use]
45
+ pub fn id(&self) -> ConstantReferenceId {
46
+ ConstantReferenceId::from(&format!(
47
+ "{}:{}:{}-{}",
48
+ self.name_id,
49
+ self.uri_id,
50
+ self.offset.start(),
51
+ self.offset.end()
52
+ ))
53
+ }
54
+ }
55
+
56
+ /// A reference to a method
57
+ #[derive(Debug)]
58
+ pub struct MethodRef {
59
+ /// The unqualified name of the method
60
+ str: StringId,
61
+ /// The document where we found the reference
62
+ uri_id: UriId,
63
+ /// The offsets inside of the document where we found the reference
64
+ offset: Offset,
65
+ /// The receiver of the method call if it's a constant
66
+ receiver: Option<NameId>,
67
+ }
68
+ assert_mem_size!(MethodRef, 32);
69
+
70
+ impl MethodRef {
71
+ #[must_use]
72
+ pub fn new(str: StringId, uri_id: UriId, offset: Offset, receiver: Option<NameId>) -> Self {
73
+ Self {
74
+ str,
75
+ uri_id,
76
+ offset,
77
+ receiver,
78
+ }
79
+ }
80
+
81
+ #[must_use]
82
+ pub fn str(&self) -> &StringId {
83
+ &self.str
84
+ }
85
+
86
+ #[must_use]
87
+ pub fn uri_id(&self) -> UriId {
88
+ self.uri_id
89
+ }
90
+
91
+ #[must_use]
92
+ pub fn offset(&self) -> &Offset {
93
+ &self.offset
94
+ }
95
+
96
+ #[must_use]
97
+ pub fn receiver(&self) -> Option<NameId> {
98
+ self.receiver
99
+ }
100
+
101
+ #[must_use]
102
+ pub fn id(&self) -> MethodReferenceId {
103
+ MethodReferenceId::from(&format!(
104
+ "{}:{}:{}-{}",
105
+ self.str,
106
+ self.uri_id,
107
+ self.offset.start(),
108
+ self.offset.end()
109
+ ))
110
+ }
111
+ }
@@ -0,0 +1,50 @@
1
+ use std::ops::Deref;
2
+
3
+ /// A reference-counted string used in the graph.
4
+ ///
5
+ /// This struct wraps a `String` with a reference count to track how many times
6
+ /// the string is used across the graph. When a document is removed, we decrement
7
+ /// the reference count for each string it uses, and remove the string from the
8
+ /// graph when its count reaches zero.
9
+ #[derive(Debug)]
10
+ pub struct StringRef {
11
+ value: String,
12
+ ref_count: u32,
13
+ }
14
+
15
+ impl StringRef {
16
+ #[must_use]
17
+ pub fn new(value: String) -> Self {
18
+ Self { value, ref_count: 1 }
19
+ }
20
+
21
+ #[must_use]
22
+ pub fn ref_count(&self) -> u32 {
23
+ self.ref_count
24
+ }
25
+
26
+ /// # Panics
27
+ ///
28
+ /// This function will panic if the reference count would exceed `u32::MAX`
29
+ pub fn increment_ref_count(&mut self, count: u32) {
30
+ self.ref_count = self
31
+ .ref_count
32
+ .checked_add(count)
33
+ .expect("Should not exceed maximum string ref count");
34
+ }
35
+
36
+ #[must_use]
37
+ pub fn decrement_ref_count(&mut self) -> bool {
38
+ debug_assert!(self.ref_count > 0);
39
+ self.ref_count -= 1;
40
+ self.ref_count > 0
41
+ }
42
+ }
43
+
44
+ impl Deref for StringRef {
45
+ type Target = String;
46
+
47
+ fn deref(&self) -> &Self::Target {
48
+ &self.value
49
+ }
50
+ }
@@ -0,0 +1,41 @@
1
+ use core::fmt;
2
+ use std::fmt::Display;
3
+
4
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5
+ pub enum Visibility {
6
+ Public,
7
+ Protected,
8
+ Private,
9
+ ModuleFunction,
10
+ }
11
+
12
+ impl Visibility {
13
+ /// Parse a visibility from a string.
14
+ ///
15
+ /// Valid values are `public`, `protected`, and `private`.
16
+ ///
17
+ /// # Panics
18
+ ///
19
+ /// Panics if the string is not a valid visibility
20
+ #[must_use]
21
+ pub fn from_string(str: &str) -> Self {
22
+ match str {
23
+ "public" => Self::Public,
24
+ "protected" => Self::Protected,
25
+ "private" => Self::Private,
26
+ "module_function" => Self::ModuleFunction,
27
+ _ => panic!("Invalid visibility: {str}"),
28
+ }
29
+ }
30
+ }
31
+
32
+ impl Display for Visibility {
33
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34
+ match self {
35
+ Self::Public => write!(f, "public"),
36
+ Self::Protected => write!(f, "protected"),
37
+ Self::Private => write!(f, "private"),
38
+ Self::ModuleFunction => write!(f, "module_function"),
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,15 @@
1
+ pub mod built_in;
2
+ pub mod comment;
3
+ pub mod declaration;
4
+ pub mod definitions;
5
+ pub mod document;
6
+ pub mod encoding;
7
+ pub mod graph;
8
+ pub mod id;
9
+ pub mod identity_maps;
10
+ pub mod ids;
11
+ pub mod keywords;
12
+ pub mod name;
13
+ pub mod references;
14
+ pub mod string_ref;
15
+ pub mod visibility;
@@ -0,0 +1,147 @@
1
+ //! Offset handling for byte-based file positions.
2
+ //!
3
+ //! This module provides the [`Offset`] struct which represents a span of bytes
4
+ //! within a file. It can be used to track positions in source code and convert
5
+ //! between byte offsets and line/column positions.
6
+
7
+ use crate::model::document::Document;
8
+
9
+ /// Represents a byte offset range within a specific file.
10
+ ///
11
+ /// An `Offset` tracks a contiguous span of bytes from `start` to `end` within a file. This is useful for
12
+ /// representing the location of tokens, AST nodes, or other text spans in source code.
13
+ #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
14
+ pub struct Offset {
15
+ /// The starting byte offset (inclusive)
16
+ start: u32,
17
+ /// The ending byte offset (exclusive)
18
+ end: u32,
19
+ }
20
+
21
+ impl Offset {
22
+ /// Creates a new `Offset` with the specified file ID and byte range.
23
+ ///
24
+ /// # Arguments
25
+ ///
26
+ /// * `start` - The starting byte position (inclusive)
27
+ /// * `end` - The ending byte position (exclusive)
28
+ #[must_use]
29
+ pub const fn new(start: u32, end: u32) -> Self {
30
+ Self { start, end }
31
+ }
32
+
33
+ /// # Panics
34
+ ///
35
+ /// This function can panic if the Prism location offsets do not fit into a u32
36
+ #[must_use]
37
+ pub fn from_prism_location(location: &ruby_prism::Location) -> Self {
38
+ Self::new(
39
+ location
40
+ .start_offset()
41
+ .try_into()
42
+ .expect("Expected usize `start` to fit in `u32`"),
43
+ location
44
+ .end_offset()
45
+ .try_into()
46
+ .expect("Expected usize `end` to fit in `u32`"),
47
+ )
48
+ }
49
+
50
+ /// # Panics
51
+ ///
52
+ /// This function can panic if the RBS location offsets don't fit into u32
53
+ #[must_use]
54
+ pub fn from_rbs_location(location: &ruby_rbs::node::RBSLocationRange) -> Self {
55
+ Self::new(
56
+ location
57
+ .start()
58
+ .try_into()
59
+ .expect("RBS location start offset should fit into u32"),
60
+ location
61
+ .end()
62
+ .try_into()
63
+ .expect("RBS location end offset should fit into u32"),
64
+ )
65
+ }
66
+
67
+ #[must_use]
68
+ pub fn start(&self) -> u32 {
69
+ self.start
70
+ }
71
+
72
+ #[must_use]
73
+ pub fn end(&self) -> u32 {
74
+ self.end
75
+ }
76
+
77
+ /// Converts an offset to a display range like `1:1-1:5`
78
+ #[must_use]
79
+ pub fn to_display_range(&self, document: &Document) -> String {
80
+ let loc = self.to_location(document).to_presentation();
81
+ format!(
82
+ "{}:{}-{}:{}",
83
+ loc.start_line(),
84
+ loc.start_col(),
85
+ loc.end_line(),
86
+ loc.end_col()
87
+ )
88
+ }
89
+
90
+ /// Converts this offset to a 0-indexed [`Location`] with start and end line/column numbers.
91
+ #[must_use]
92
+ pub fn to_location(&self, document: &Document) -> Location {
93
+ let line_index = document.line_index();
94
+ let start = line_index.line_col(self.start().into());
95
+ let end = line_index.line_col(self.end().into());
96
+ Location {
97
+ start_line: start.line,
98
+ start_col: start.col,
99
+ end_line: end.line,
100
+ end_col: end.col,
101
+ }
102
+ }
103
+ }
104
+
105
+ /// A resolved location within a file, with start and end line/column positions.
106
+ /// Values are 0-indexed by default. Use [`to_presentation`](Location::to_presentation)
107
+ /// for 1-indexed values suitable for display.
108
+ #[derive(Debug, Clone, PartialEq, Eq)]
109
+ pub struct Location {
110
+ start_line: u32,
111
+ start_col: u32,
112
+ end_line: u32,
113
+ end_col: u32,
114
+ }
115
+
116
+ impl Location {
117
+ #[must_use]
118
+ pub fn start_line(&self) -> u32 {
119
+ self.start_line
120
+ }
121
+
122
+ #[must_use]
123
+ pub fn start_col(&self) -> u32 {
124
+ self.start_col
125
+ }
126
+
127
+ #[must_use]
128
+ pub fn end_line(&self) -> u32 {
129
+ self.end_line
130
+ }
131
+
132
+ #[must_use]
133
+ pub fn end_col(&self) -> u32 {
134
+ self.end_col
135
+ }
136
+
137
+ /// Returns a new `Location` with 1-indexed values for display purposes.
138
+ #[must_use]
139
+ pub fn to_presentation(&self) -> Self {
140
+ Self {
141
+ start_line: self.start_line + 1,
142
+ start_col: self.start_col + 1,
143
+ end_line: self.end_line + 1,
144
+ end_col: self.end_col + 1,
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,6 @@
1
+ use line_index::LineCol;
2
+
3
+ /// A position composed of line and columns. Note that all values are zero indexed and columns are based on the LSP
4
+ /// specification, meaning that they are always based in code units and can be retrieved for the 3 supported encodings
5
+ /// (Utf8, Utf16, Utf32)
6
+ pub type Position = LineCol;