rubydex 0.1.0.beta1-x86_64-linux → 0.1.0.beta2-x86_64-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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rubydex/declaration.c +146 -0
  3. data/ext/rubydex/declaration.h +10 -0
  4. data/ext/rubydex/definition.c +234 -0
  5. data/ext/rubydex/definition.h +28 -0
  6. data/ext/rubydex/diagnostic.c +6 -0
  7. data/ext/rubydex/diagnostic.h +11 -0
  8. data/ext/rubydex/document.c +98 -0
  9. data/ext/rubydex/document.h +10 -0
  10. data/ext/rubydex/extconf.rb +36 -15
  11. data/ext/rubydex/graph.c +405 -0
  12. data/ext/rubydex/graph.h +10 -0
  13. data/ext/rubydex/handle.h +44 -0
  14. data/ext/rubydex/location.c +22 -0
  15. data/ext/rubydex/location.h +15 -0
  16. data/ext/rubydex/reference.c +104 -0
  17. data/ext/rubydex/reference.h +16 -0
  18. data/ext/rubydex/rubydex.c +22 -0
  19. data/ext/rubydex/utils.c +27 -0
  20. data/ext/rubydex/utils.h +13 -0
  21. data/lib/rubydex/3.2/rubydex.so +0 -0
  22. data/lib/rubydex/3.3/rubydex.so +0 -0
  23. data/lib/rubydex/3.4/rubydex.so +0 -0
  24. data/lib/rubydex/4.0/rubydex.so +0 -0
  25. data/lib/rubydex/librubydex_sys.so +0 -0
  26. data/lib/rubydex/version.rb +1 -1
  27. data/rust/Cargo.lock +1275 -0
  28. data/rust/Cargo.toml +23 -0
  29. data/rust/about.hbs +78 -0
  30. data/rust/about.toml +9 -0
  31. data/rust/rubydex/Cargo.toml +41 -0
  32. data/rust/rubydex/src/diagnostic.rs +108 -0
  33. data/rust/rubydex/src/errors.rs +28 -0
  34. data/rust/rubydex/src/indexing/local_graph.rs +172 -0
  35. data/rust/rubydex/src/indexing/ruby_indexer.rs +5397 -0
  36. data/rust/rubydex/src/indexing.rs +128 -0
  37. data/rust/rubydex/src/job_queue.rs +186 -0
  38. data/rust/rubydex/src/lib.rs +15 -0
  39. data/rust/rubydex/src/listing.rs +249 -0
  40. data/rust/rubydex/src/main.rs +116 -0
  41. data/rust/rubydex/src/model/comment.rs +24 -0
  42. data/rust/rubydex/src/model/declaration.rs +541 -0
  43. data/rust/rubydex/src/model/definitions.rs +1475 -0
  44. data/rust/rubydex/src/model/document.rs +111 -0
  45. data/rust/rubydex/src/model/encoding.rs +22 -0
  46. data/rust/rubydex/src/model/graph.rs +1387 -0
  47. data/rust/rubydex/src/model/id.rs +90 -0
  48. data/rust/rubydex/src/model/identity_maps.rs +54 -0
  49. data/rust/rubydex/src/model/ids.rs +32 -0
  50. data/rust/rubydex/src/model/name.rs +188 -0
  51. data/rust/rubydex/src/model/references.rs +129 -0
  52. data/rust/rubydex/src/model/string_ref.rs +44 -0
  53. data/rust/rubydex/src/model/visibility.rs +41 -0
  54. data/rust/rubydex/src/model.rs +13 -0
  55. data/rust/rubydex/src/offset.rs +70 -0
  56. data/rust/rubydex/src/position.rs +6 -0
  57. data/rust/rubydex/src/query.rs +103 -0
  58. data/rust/rubydex/src/resolution.rs +4421 -0
  59. data/rust/rubydex/src/stats/memory.rs +71 -0
  60. data/rust/rubydex/src/stats/timer.rs +126 -0
  61. data/rust/rubydex/src/stats.rs +9 -0
  62. data/rust/rubydex/src/test_utils/context.rs +226 -0
  63. data/rust/rubydex/src/test_utils/graph_test.rs +229 -0
  64. data/rust/rubydex/src/test_utils/local_graph_test.rs +166 -0
  65. data/rust/rubydex/src/test_utils.rs +52 -0
  66. data/rust/rubydex/src/visualization/dot.rs +176 -0
  67. data/rust/rubydex/src/visualization.rs +6 -0
  68. data/rust/rubydex/tests/cli.rs +167 -0
  69. data/rust/rubydex-sys/Cargo.toml +20 -0
  70. data/rust/rubydex-sys/build.rs +14 -0
  71. data/rust/rubydex-sys/cbindgen.toml +12 -0
  72. data/rust/rubydex-sys/src/declaration_api.rs +114 -0
  73. data/rust/rubydex-sys/src/definition_api.rs +350 -0
  74. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
  75. data/rust/rubydex-sys/src/document_api.rs +54 -0
  76. data/rust/rubydex-sys/src/graph_api.rs +493 -0
  77. data/rust/rubydex-sys/src/lib.rs +9 -0
  78. data/rust/rubydex-sys/src/location_api.rs +79 -0
  79. data/rust/rubydex-sys/src/name_api.rs +81 -0
  80. data/rust/rubydex-sys/src/reference_api.rs +191 -0
  81. data/rust/rubydex-sys/src/utils.rs +50 -0
  82. data/rust/rustfmt.toml +2 -0
  83. metadata +77 -2
@@ -0,0 +1,90 @@
1
+ //! This module contains stable ID representations that compose the `Graph` global representation
2
+
3
+ use std::{
4
+ hash::{Hash, Hasher},
5
+ marker::PhantomData,
6
+ ops::Deref,
7
+ };
8
+ use xxhash_rust::xxh3::xxh3_64;
9
+
10
+ /// A stable ID representation using i64.
11
+ ///
12
+ /// We use i64 instead of u64 because `SQLite` doesn't support unsigned integers.
13
+ /// IDs are generated from `xxh3_64` hashes (u64) then cast to i64, preserving all bits.
14
+ /// Negative values are expected and normal - not a sign of memory corruption.
15
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
16
+ pub struct Id<T> {
17
+ value: i64,
18
+ _marker: PhantomData<T>,
19
+ }
20
+
21
+ impl<T> Id<T> {
22
+ #[must_use]
23
+ pub fn new(value: i64) -> Self {
24
+ Self {
25
+ value,
26
+ _marker: PhantomData,
27
+ }
28
+ }
29
+ }
30
+
31
+ impl<T> Deref for Id<T> {
32
+ type Target = i64;
33
+
34
+ fn deref(&self) -> &Self::Target {
35
+ &self.value
36
+ }
37
+ }
38
+
39
+ impl<T> std::fmt::Display for Id<T> {
40
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41
+ write!(f, "{}", self.value)
42
+ }
43
+ }
44
+
45
+ impl<T> Hash for Id<T> {
46
+ fn hash<H: Hasher>(&self, state: &mut H) {
47
+ state.write_i64(self.value);
48
+ }
49
+ }
50
+
51
+ impl<T> From<&str> for Id<T> {
52
+ fn from(value: &str) -> Self {
53
+ let hash = xxh3_64(value.as_bytes());
54
+ Self::new(hash.cast_signed())
55
+ }
56
+ }
57
+
58
+ impl<T> From<&String> for Id<T> {
59
+ fn from(value: &String) -> Self {
60
+ let hash = xxh3_64(value.as_bytes());
61
+ Self::new(hash.cast_signed())
62
+ }
63
+ }
64
+
65
+ #[cfg(test)]
66
+ mod tests {
67
+ use super::*;
68
+
69
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
70
+ pub struct Marker;
71
+ pub type TestId = Id<Marker>;
72
+
73
+ #[test]
74
+ fn test_create_hash() {
75
+ // Same input should produce same hash (deterministic)
76
+ let id1 = TestId::from("test_input");
77
+ let id2 = TestId::from("test_input");
78
+ assert_eq!(id1, id2);
79
+
80
+ // Different inputs should produce different hashes (unique)
81
+ let id3 = TestId::from("different_input");
82
+ assert_ne!(id1, id3);
83
+ }
84
+
85
+ #[test]
86
+ fn deref_unwraps_value() {
87
+ let id = TestId::new(123);
88
+ assert_eq!(*id, 123);
89
+ }
90
+ }
@@ -0,0 +1,54 @@
1
+ //! This module contains identity maps that use externally hashed IDs as keys. They are used to avoid hashing the same
2
+ //! value twice, simply using the given key directly
3
+
4
+ use std::{
5
+ collections::{HashMap, HashSet},
6
+ hash::{BuildHasher, Hasher},
7
+ };
8
+
9
+ #[derive(Default)]
10
+ pub struct IdentityHasher {
11
+ hash: u64,
12
+ }
13
+
14
+ impl Hasher for IdentityHasher {
15
+ fn write(&mut self, _bytes: &[u8]) {
16
+ unreachable!("IdentityHasher only supports write_u64");
17
+ }
18
+
19
+ fn write_u64(&mut self, i: u64) {
20
+ self.hash = i;
21
+ }
22
+
23
+ fn finish(&self) -> u64 {
24
+ self.hash
25
+ }
26
+ }
27
+
28
+ #[derive(Default)]
29
+ pub struct IdentityHashBuilder;
30
+
31
+ impl BuildHasher for IdentityHashBuilder {
32
+ type Hasher = IdentityHasher;
33
+
34
+ fn build_hasher(&self) -> Self::Hasher {
35
+ IdentityHasher::default()
36
+ }
37
+ }
38
+
39
+ pub type IdentityHashMap<K, V> = HashMap<K, V, IdentityHashBuilder>;
40
+ pub type IdentityHashSet<T> = HashSet<T, IdentityHashBuilder>;
41
+
42
+ #[cfg(test)]
43
+ mod tests {
44
+ use super::*;
45
+
46
+ #[test]
47
+ fn identity_hasher_uses_value_as_is() {
48
+ let builder = IdentityHashBuilder;
49
+ let mut hasher = builder.build_hasher();
50
+
51
+ hasher.write_u64(42);
52
+ assert_eq!(hasher.finish(), 42);
53
+ }
54
+ }
@@ -0,0 +1,32 @@
1
+ use crate::model::id::Id;
2
+
3
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
4
+ pub struct DeclarationMarker;
5
+ /// `DeclarationId` represents the ID of a fully qualified name. For example, `Foo::Bar` or `Foo#my_method`
6
+ pub type DeclarationId = Id<DeclarationMarker>;
7
+
8
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
9
+ pub struct DefinitionMarker;
10
+ // DefinitionId represents the ID of a definition found in a specific file
11
+ pub type DefinitionId = Id<DefinitionMarker>;
12
+
13
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
14
+ pub struct UriMarker;
15
+ // UriId represents the ID of a URI, which is the unique identifier for a document
16
+ pub type UriId = Id<UriMarker>;
17
+
18
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
19
+ pub struct StringMarker;
20
+ /// `StringId` represents an ID for an interned string value
21
+ pub type StringId = Id<StringMarker>;
22
+
23
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
24
+ pub struct ReferenceMarker;
25
+ /// `ReferenceId` represents the ID of a reference occurrence in a file.
26
+ /// It is built from the reference kind, `uri_id` and the reference `offset`.
27
+ pub type ReferenceId = Id<ReferenceMarker>;
28
+
29
+ #[derive(PartialEq, Eq, Debug, Clone, Copy)]
30
+ pub struct NameMarker;
31
+ /// `NameId` represents an ID for any constant name that we find as part of a reference or definition
32
+ pub type NameId = Id<NameMarker>;
@@ -0,0 +1,188 @@
1
+ use crate::model::ids::{DeclarationId, NameId, StringId};
2
+
3
+ #[derive(Debug, Clone)]
4
+ pub struct Name {
5
+ /// The unqualified name of the constant
6
+ str: StringId,
7
+ /// The ID of parent scope for this constant. For example:
8
+ ///
9
+ /// ```ruby
10
+ /// Foo::Bar::Baz
11
+ /// # ^ parent scope of Bar::Baz
12
+ /// # ^ parent scope of Baz
13
+ /// ```
14
+ ///
15
+ /// `None` indicates that this is a simple constant read or a top level reference
16
+ parent_scope: Option<NameId>,
17
+ /// The ID of the name for the nesting where we found this name. This effectively turns the structure into a linked
18
+ /// list of names to represent the nesting
19
+ nesting: Option<NameId>,
20
+ ref_count: usize,
21
+ }
22
+
23
+ impl Name {
24
+ #[must_use]
25
+ pub fn new(str: StringId, parent_scope: Option<NameId>, nesting: Option<NameId>) -> Self {
26
+ Self {
27
+ str,
28
+ parent_scope,
29
+ nesting,
30
+ ref_count: 1,
31
+ }
32
+ }
33
+
34
+ #[must_use]
35
+ pub fn str(&self) -> &StringId {
36
+ &self.str
37
+ }
38
+
39
+ #[must_use]
40
+ pub fn parent_scope(&self) -> &Option<NameId> {
41
+ &self.parent_scope
42
+ }
43
+
44
+ #[must_use]
45
+ pub fn nesting(&self) -> &Option<NameId> {
46
+ &self.nesting
47
+ }
48
+
49
+ #[must_use]
50
+ pub fn id(&self) -> NameId {
51
+ NameId::from(&format!(
52
+ "{}{}{}",
53
+ self.str,
54
+ self.parent_scope.map_or(String::from("None"), |id| id.to_string()),
55
+ self.nesting.map_or(String::from("None"), |id| id.to_string())
56
+ ))
57
+ }
58
+ }
59
+
60
+ #[derive(Debug, Clone)]
61
+ pub struct ResolvedName {
62
+ name: Name,
63
+ declaration_id: DeclarationId,
64
+ }
65
+
66
+ impl ResolvedName {
67
+ #[must_use]
68
+ pub fn new(name: Name, declaration_id: DeclarationId) -> Self {
69
+ Self { name, declaration_id }
70
+ }
71
+
72
+ #[must_use]
73
+ pub fn name(&self) -> &Name {
74
+ &self.name
75
+ }
76
+
77
+ #[must_use]
78
+ pub fn declaration_id(&self) -> &DeclarationId {
79
+ &self.declaration_id
80
+ }
81
+ }
82
+
83
+ /// A usage of a constant name. This could be a constant reference or a definition like a class or module
84
+ #[derive(Debug, Clone)]
85
+ pub enum NameRef {
86
+ /// This name has not yet been resolved. We don't yet know what this name refers to or if it refers to an existing
87
+ /// declaration
88
+ Unresolved(Box<Name>),
89
+ /// This name has been resolved to an existing declaration
90
+ Resolved(Box<ResolvedName>),
91
+ }
92
+
93
+ impl NameRef {
94
+ #[must_use]
95
+ pub fn str(&self) -> &StringId {
96
+ match self {
97
+ NameRef::Unresolved(name) => name.str(),
98
+ NameRef::Resolved(resolved_name) => resolved_name.name.str(),
99
+ }
100
+ }
101
+
102
+ #[must_use]
103
+ pub fn parent_scope(&self) -> &Option<NameId> {
104
+ match self {
105
+ NameRef::Unresolved(name) => name.parent_scope(),
106
+ NameRef::Resolved(resolved_name) => resolved_name.name.parent_scope(),
107
+ }
108
+ }
109
+
110
+ #[must_use]
111
+ pub fn into_unresolved(self) -> Option<Name> {
112
+ match self {
113
+ NameRef::Unresolved(name) => Some(*name),
114
+ NameRef::Resolved(_) => None,
115
+ }
116
+ }
117
+
118
+ #[must_use]
119
+ pub fn nesting(&self) -> &Option<NameId> {
120
+ match self {
121
+ NameRef::Unresolved(name) => name.nesting(),
122
+ NameRef::Resolved(resolved_name) => resolved_name.name.nesting(),
123
+ }
124
+ }
125
+
126
+ #[must_use]
127
+ pub fn ref_count(&self) -> usize {
128
+ match self {
129
+ NameRef::Unresolved(name) => name.ref_count,
130
+ NameRef::Resolved(resolved_name) => resolved_name.name.ref_count,
131
+ }
132
+ }
133
+
134
+ pub fn increment_ref_count(&mut self, count: usize) {
135
+ match self {
136
+ NameRef::Unresolved(name) => name.ref_count += count,
137
+ NameRef::Resolved(resolved_name) => resolved_name.name.ref_count += count,
138
+ }
139
+ }
140
+
141
+ #[must_use]
142
+ pub fn decrement_ref_count(&mut self) -> bool {
143
+ match self {
144
+ NameRef::Unresolved(name) => {
145
+ name.ref_count -= 1;
146
+ name.ref_count > 0
147
+ }
148
+ NameRef::Resolved(resolved_name) => {
149
+ resolved_name.name.ref_count -= 1;
150
+ resolved_name.name.ref_count > 0
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ #[cfg(test)]
157
+ mod tests {
158
+ use super::*;
159
+
160
+ #[test]
161
+ fn same_parent_scope_and_nesting() {
162
+ let name_1 = Name::new(StringId::from("Foo"), None, None);
163
+ let name_2 = Name::new(StringId::from("Foo"), None, None);
164
+ assert_eq!(name_1.id(), name_2.id());
165
+
166
+ let name_3 = Name::new(StringId::from("Foo"), Some(name_1.id()), None);
167
+ let name_4 = Name::new(StringId::from("Foo"), Some(name_2.id()), None);
168
+ assert_eq!(name_3.id(), name_4.id());
169
+
170
+ let name_5 = Name::new(StringId::from("Foo"), None, Some(name_1.id()));
171
+ let name_6 = Name::new(StringId::from("Foo"), None, Some(name_2.id()));
172
+ assert_eq!(name_5.id(), name_6.id());
173
+ assert_ne!(name_3.id(), name_5.id());
174
+ assert_ne!(name_4.id(), name_6.id());
175
+
176
+ let name_7 = Name::new(
177
+ StringId::from("Foo"),
178
+ Some(Name::new(StringId::from("Foo"), None, None).id()),
179
+ Some(Name::new(StringId::from("Foo"), None, None).id()),
180
+ );
181
+ let name_8 = Name::new(
182
+ StringId::from("Foo"),
183
+ Some(Name::new(StringId::from("Foo"), None, None).id()),
184
+ Some(Name::new(StringId::from("Foo"), None, None).id()),
185
+ );
186
+ assert_eq!(name_7.id(), name_8.id());
187
+ }
188
+ }
@@ -0,0 +1,129 @@
1
+ use crate::{
2
+ diagnostic::Diagnostic,
3
+ model::ids::{NameId, ReferenceId, 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
+ /// Diagnostics associated with this reference
17
+ diagnostics: Vec<Diagnostic>,
18
+ }
19
+
20
+ impl ConstantReference {
21
+ #[must_use]
22
+ pub fn new(name_id: NameId, uri_id: UriId, offset: Offset) -> Self {
23
+ Self {
24
+ name_id,
25
+ uri_id,
26
+ offset,
27
+ diagnostics: Vec::new(),
28
+ }
29
+ }
30
+
31
+ #[must_use]
32
+ pub fn name_id(&self) -> &NameId {
33
+ &self.name_id
34
+ }
35
+
36
+ #[must_use]
37
+ pub fn uri_id(&self) -> UriId {
38
+ self.uri_id
39
+ }
40
+
41
+ #[must_use]
42
+ pub fn offset(&self) -> &Offset {
43
+ &self.offset
44
+ }
45
+
46
+ #[must_use]
47
+ pub fn id(&self) -> ReferenceId {
48
+ // C:<uri_id>:<start>-<end>
49
+ let key = format!(
50
+ "C:{}:{}:{}-{}",
51
+ self.name_id,
52
+ self.uri_id,
53
+ self.offset.start(),
54
+ self.offset.end()
55
+ );
56
+ ReferenceId::from(&key)
57
+ }
58
+
59
+ #[must_use]
60
+ pub fn diagnostics(&self) -> &[Diagnostic] {
61
+ &self.diagnostics
62
+ }
63
+
64
+ pub fn add_diagnostic(&mut self, diagnostic: Diagnostic) {
65
+ self.diagnostics.push(diagnostic);
66
+ }
67
+ }
68
+
69
+ /// A reference to a method
70
+ #[derive(Debug)]
71
+ pub struct MethodRef {
72
+ /// The unqualified name of the method
73
+ str: StringId,
74
+ /// The document where we found the reference
75
+ uri_id: UriId,
76
+ /// The offsets inside of the document where we found the reference
77
+ offset: Offset,
78
+ /// Diagnostics associated with this reference
79
+ diagnostics: Vec<Diagnostic>,
80
+ }
81
+
82
+ impl MethodRef {
83
+ #[must_use]
84
+ pub fn new(str: StringId, uri_id: UriId, offset: Offset) -> Self {
85
+ Self {
86
+ str,
87
+ uri_id,
88
+ offset,
89
+ diagnostics: Vec::new(),
90
+ }
91
+ }
92
+
93
+ #[must_use]
94
+ pub fn str(&self) -> &StringId {
95
+ &self.str
96
+ }
97
+
98
+ #[must_use]
99
+ pub fn uri_id(&self) -> UriId {
100
+ self.uri_id
101
+ }
102
+
103
+ #[must_use]
104
+ pub fn offset(&self) -> &Offset {
105
+ &self.offset
106
+ }
107
+
108
+ #[must_use]
109
+ pub fn id(&self) -> ReferenceId {
110
+ // M:<uri_id>:<start>-<end>
111
+ let key = format!(
112
+ "M:{}:{}:{}-{}",
113
+ self.str,
114
+ self.uri_id,
115
+ self.offset.start(),
116
+ self.offset.end()
117
+ );
118
+ ReferenceId::from(&key)
119
+ }
120
+
121
+ #[must_use]
122
+ pub fn diagnostics(&self) -> &[Diagnostic] {
123
+ &self.diagnostics
124
+ }
125
+
126
+ pub fn add_diagnostic(&mut self, diagnostic: Diagnostic) {
127
+ self.diagnostics.push(diagnostic);
128
+ }
129
+ }
@@ -0,0 +1,44 @@
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: usize,
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) -> usize {
23
+ self.ref_count
24
+ }
25
+
26
+ pub fn increment_ref_count(&mut self, count: usize) {
27
+ self.ref_count += count;
28
+ }
29
+
30
+ #[must_use]
31
+ pub fn decrement_ref_count(&mut self) -> bool {
32
+ debug_assert!(self.ref_count > 0);
33
+ self.ref_count -= 1;
34
+ self.ref_count > 0
35
+ }
36
+ }
37
+
38
+ impl Deref for StringRef {
39
+ type Target = String;
40
+
41
+ fn deref(&self) -> &Self::Target {
42
+ &self.value
43
+ }
44
+ }
@@ -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,13 @@
1
+ pub mod comment;
2
+ pub mod declaration;
3
+ pub mod definitions;
4
+ pub mod document;
5
+ pub mod encoding;
6
+ pub mod graph;
7
+ pub mod id;
8
+ pub mod identity_maps;
9
+ pub mod ids;
10
+ pub mod name;
11
+ pub mod references;
12
+ pub mod string_ref;
13
+ pub mod visibility;
@@ -0,0 +1,70 @@
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
+ #[cfg(any(test, feature = "test_utils"))]
8
+ use crate::model::document::Document;
9
+
10
+ /// Represents a byte offset range within a specific file.
11
+ ///
12
+ /// An `Offset` tracks a contiguous span of bytes from `start` to `end` within a file. This is useful for
13
+ /// representing the location of tokens, AST nodes, or other text spans in source code.
14
+ #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
15
+ pub struct Offset {
16
+ /// The starting byte offset (inclusive)
17
+ start: u32,
18
+ /// The ending byte offset (exclusive)
19
+ end: u32,
20
+ }
21
+
22
+ impl Offset {
23
+ /// Creates a new `Offset` with the specified file ID and byte range.
24
+ ///
25
+ /// # Arguments
26
+ ///
27
+ /// * `start` - The starting byte position (inclusive)
28
+ /// * `end` - The ending byte position (exclusive)
29
+ #[must_use]
30
+ pub const fn new(start: u32, end: u32) -> Self {
31
+ Self { start, end }
32
+ }
33
+
34
+ /// # Panics
35
+ ///
36
+ /// This function can panic if the Prism location offsets do not fit into a u32
37
+ #[must_use]
38
+ pub fn from_prism_location(location: &ruby_prism::Location) -> Self {
39
+ Self::new(
40
+ location
41
+ .start_offset()
42
+ .try_into()
43
+ .expect("Expected usize `start` to fit in `u32`"),
44
+ location
45
+ .end_offset()
46
+ .try_into()
47
+ .expect("Expected usize `end` to fit in `u32`"),
48
+ )
49
+ }
50
+
51
+ #[must_use]
52
+ pub fn start(&self) -> u32 {
53
+ self.start
54
+ }
55
+
56
+ #[must_use]
57
+ pub fn end(&self) -> u32 {
58
+ self.end
59
+ }
60
+
61
+ /// Converts an offset to a display range like `1:1-1:5`
62
+ #[cfg(any(test, feature = "test_utils"))]
63
+ #[must_use]
64
+ pub fn to_display_range(&self, document: &Document) -> String {
65
+ let line_index = document.line_index();
66
+ let start = line_index.line_col(self.start().into());
67
+ let end = line_index.line_col(self.end().into());
68
+ format!("{}:{}-{}:{}", start.line + 1, start.col + 1, end.line + 1, end.col + 1)
69
+ }
70
+ }
@@ -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;