rubydex 0.2.5 → 0.2.7
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 +17 -16
- data/THIRD_PARTY_LICENSES.html +45 -12
- data/exe/rdx +2 -0
- data/ext/rubydex/declaration.c +115 -106
- data/ext/rubydex/definition.c +123 -72
- data/ext/rubydex/diagnostic.c +5 -0
- data/ext/rubydex/document.c +51 -23
- data/ext/rubydex/extconf.rb +1 -1
- data/ext/rubydex/graph.c +253 -66
- data/ext/rubydex/handle.h +34 -5
- data/ext/rubydex/location.c +5 -0
- data/ext/rubydex/reference.c +51 -46
- data/ext/rubydex/rubydex.c +5 -0
- data/ext/rubydex/signature.c +5 -0
- data/ext/rubydex/utils.c +13 -0
- data/ext/rubydex/utils.h +4 -0
- data/lib/rubydex/bin/rubydex_mcp.exe +0 -0
- data/lib/rubydex/errors.rb +11 -0
- data/lib/rubydex/graph.rb +9 -28
- data/lib/rubydex/location.rb +24 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +1 -0
- data/rbi/rubydex.rbi +40 -15
- data/rust/Cargo.lock +122 -17
- data/rust/rubydex/Cargo.toml +26 -2
- data/rust/rubydex/benches/graph_memory.rs +46 -0
- data/rust/rubydex/src/config.rs +275 -0
- data/rust/rubydex/src/dot.rs +609 -0
- data/rust/rubydex/src/errors.rs +2 -0
- data/rust/rubydex/src/indexing/rbs_indexer.rs +19 -1
- data/rust/rubydex/src/indexing/ruby_indexer.rs +4 -0
- data/rust/rubydex/src/lib.rs +8 -1
- data/rust/rubydex/src/main.rs +8 -5
- data/rust/rubydex/src/model/built_in.rs +5 -2
- data/rust/rubydex/src/model/comment.rs +2 -0
- data/rust/rubydex/src/model/declaration.rs +13 -51
- data/rust/rubydex/src/model/definitions.rs +13 -1
- data/rust/rubydex/src/model/document.rs +2 -0
- data/rust/rubydex/src/model/encoding.rs +2 -0
- data/rust/rubydex/src/model/graph.rs +88 -27
- data/rust/rubydex/src/model/identity_maps.rs +3 -0
- data/rust/rubydex/src/model/keywords.rs +3 -0
- data/rust/rubydex/src/model/name.rs +2 -0
- data/rust/rubydex/src/model/string_ref.rs +2 -0
- data/rust/rubydex/src/model/visibility.rs +3 -0
- data/rust/rubydex/src/operation/applier.rs +1 -0
- data/rust/rubydex/src/operation/mod.rs +1 -0
- data/rust/rubydex/src/operation/ruby_builder.rs +4 -0
- data/rust/rubydex/src/query.rs +114 -33
- data/rust/rubydex/src/resolution.rs +18 -20
- data/rust/rubydex/src/resolution_tests.rs +132 -0
- data/rust/rubydex/tests/cli.rs +17 -61
- data/rust/rubydex-mcp/Cargo.toml +9 -3
- data/rust/rubydex-sys/Cargo.toml +9 -2
- data/rust/rubydex-sys/src/definition_api.rs +72 -2
- data/rust/rubydex-sys/src/document_api.rs +28 -0
- data/rust/rubydex-sys/src/graph_api.rs +74 -6
- metadata +6 -4
- data/rust/rubydex/src/visualization/dot.rs +0 -192
- data/rust/rubydex/src/visualization.rs +0 -6
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
use std::collections::HashSet;
|
|
2
2
|
use std::collections::hash_map::Entry;
|
|
3
|
-
use std::path::PathBuf;
|
|
3
|
+
use std::path::{Path, PathBuf};
|
|
4
4
|
|
|
5
|
+
use crate::assert_mem_size;
|
|
6
|
+
use crate::config::Config;
|
|
5
7
|
use crate::diagnostic::Diagnostic;
|
|
8
|
+
use crate::errors::Errors;
|
|
6
9
|
use crate::indexing::local_graph::LocalGraph;
|
|
7
10
|
use crate::model::built_in::{OBJECT_ID, add_built_in_data};
|
|
8
11
|
use crate::model::declaration::{Ancestor, Declaration, Namespace};
|
|
@@ -28,6 +31,7 @@ pub enum NameDependent {
|
|
|
28
31
|
/// This name's `nesting` is the key name — reference-only dependency.
|
|
29
32
|
NestedName(NameId),
|
|
30
33
|
}
|
|
34
|
+
assert_mem_size!(NameDependent, 16);
|
|
31
35
|
|
|
32
36
|
/// Items processed by the unified invalidation worklist.
|
|
33
37
|
enum InvalidationItem {
|
|
@@ -38,6 +42,7 @@ enum InvalidationItem {
|
|
|
38
42
|
/// Ancestor context changed — unresolve references under this name but keep the name resolved.
|
|
39
43
|
References(NameId),
|
|
40
44
|
}
|
|
45
|
+
assert_mem_size!(InvalidationItem, 16);
|
|
41
46
|
|
|
42
47
|
/// A work item produced by graph mutations (update/delete) that needs resolution.
|
|
43
48
|
#[derive(Debug)]
|
|
@@ -49,6 +54,7 @@ pub enum Unit {
|
|
|
49
54
|
/// A declaration whose ancestors need re-linearization
|
|
50
55
|
Ancestors(DeclarationId),
|
|
51
56
|
}
|
|
57
|
+
assert_mem_size!(Unit, 16);
|
|
52
58
|
|
|
53
59
|
// The `Graph` is the global representation of the entire Ruby codebase. It contains all declarations and their
|
|
54
60
|
// relationships
|
|
@@ -81,9 +87,10 @@ pub struct Graph {
|
|
|
81
87
|
/// Drained by `take_pending_work()` before resolution.
|
|
82
88
|
pending_work: Vec<Unit>,
|
|
83
89
|
|
|
84
|
-
///
|
|
85
|
-
|
|
90
|
+
/// Project configuration
|
|
91
|
+
config: Config,
|
|
86
92
|
}
|
|
93
|
+
assert_mem_size!(Graph, 352);
|
|
87
94
|
|
|
88
95
|
impl Graph {
|
|
89
96
|
#[must_use]
|
|
@@ -99,7 +106,7 @@ impl Graph {
|
|
|
99
106
|
position_encoding: Encoding::default(),
|
|
100
107
|
name_dependents: IdentityHashMap::default(),
|
|
101
108
|
pending_work: Vec::default(),
|
|
102
|
-
|
|
109
|
+
config: Config::new(),
|
|
103
110
|
};
|
|
104
111
|
|
|
105
112
|
add_built_in_data(&mut graph);
|
|
@@ -121,13 +128,40 @@ impl Graph {
|
|
|
121
128
|
/// Adds paths to exclude from file discovery during indexing. Excluded directories will be skipped entirely during
|
|
122
129
|
/// directory traversal.
|
|
123
130
|
pub fn exclude_paths(&mut self, paths: Vec<PathBuf>) {
|
|
124
|
-
self.
|
|
131
|
+
self.config.exclude_paths(paths);
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
/// Returns the set of paths excluded from file discovery.
|
|
128
135
|
#[must_use]
|
|
129
|
-
pub fn excluded_paths(&self) ->
|
|
130
|
-
|
|
136
|
+
pub fn excluded_paths(&self) -> HashSet<PathBuf> {
|
|
137
|
+
self.config.excluded_paths()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// Returns the root directory of the workspace being indexed.
|
|
141
|
+
#[must_use]
|
|
142
|
+
pub fn workspace_path(&self) -> &Path {
|
|
143
|
+
self.config.workspace_path()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// Sets the root directory of the workspace being indexed.
|
|
147
|
+
pub fn set_workspace_path(&mut self, workspace_path: PathBuf) {
|
|
148
|
+
self.config.set_workspace_path(workspace_path);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// Loads a configuration file. Pass `None` to load the default `rubydex.toml` configuration file if it exists
|
|
152
|
+
///
|
|
153
|
+
/// # Errors
|
|
154
|
+
///
|
|
155
|
+
/// Returns an [`Errors::ConfigNotFound`] if an explicitly requested file does not exist or an
|
|
156
|
+
/// [`Errors::ConfigError`] if a file cannot otherwise be read or its contents are malformed.
|
|
157
|
+
pub fn load_config(&mut self, config_path: Option<&Path>) -> Result<(), Errors> {
|
|
158
|
+
match config_path {
|
|
159
|
+
Some(path) => {
|
|
160
|
+
let path = self.config.workspace_path().join(path);
|
|
161
|
+
self.config.load_file(&path)
|
|
162
|
+
}
|
|
163
|
+
None => self.config.load_default(),
|
|
164
|
+
}
|
|
131
165
|
}
|
|
132
166
|
|
|
133
167
|
/// # Panics
|
|
@@ -306,9 +340,6 @@ impl Graph {
|
|
|
306
340
|
self.definition_to_declaration_id(self.definitions.get(&definition_id).unwrap())
|
|
307
341
|
}
|
|
308
342
|
|
|
309
|
-
/// # Panics
|
|
310
|
-
///
|
|
311
|
-
/// Panics if the definition is not found
|
|
312
343
|
#[must_use]
|
|
313
344
|
pub fn definition_to_declaration_id(&self, definition: &Definition) -> Option<&DeclarationId> {
|
|
314
345
|
let (nesting_name_id, member_str_id) = match definition {
|
|
@@ -442,20 +473,21 @@ impl Graph {
|
|
|
442
473
|
}
|
|
443
474
|
|
|
444
475
|
/// Looks up the declaration for a `SelfReceiver` method/alias through the singleton class.
|
|
476
|
+
///
|
|
477
|
+
/// Returns `None` when the owner cannot be resolved to a namespace with a singleton class. This
|
|
478
|
+
/// can happen when the enclosing construct resolved to a non-namespace declaration (e.g. a
|
|
479
|
+
/// constant or constant alias that a same-named `class`/`module` reopened without promotion), in
|
|
480
|
+
/// which case the method has no owning declaration.
|
|
445
481
|
fn find_self_receiver_declaration(&self, def_id: DefinitionId, member_str_id: StringId) -> Option<&DeclarationId> {
|
|
446
482
|
let owner_decl_id = self.definition_id_to_declaration_id(def_id)?;
|
|
447
483
|
let singleton_id = self
|
|
448
484
|
.declarations
|
|
449
|
-
.get(owner_decl_id)
|
|
450
|
-
.
|
|
451
|
-
.as_namespace()
|
|
452
|
-
.unwrap()
|
|
485
|
+
.get(owner_decl_id)?
|
|
486
|
+
.as_namespace()?
|
|
453
487
|
.singleton_class()?;
|
|
454
488
|
self.declarations
|
|
455
|
-
.get(singleton_id)
|
|
456
|
-
.
|
|
457
|
-
.as_namespace()
|
|
458
|
-
.unwrap()
|
|
489
|
+
.get(singleton_id)?
|
|
490
|
+
.as_namespace()?
|
|
459
491
|
.member(&member_str_id)
|
|
460
492
|
}
|
|
461
493
|
|
|
@@ -483,10 +515,7 @@ impl Graph {
|
|
|
483
515
|
|
|
484
516
|
#[must_use]
|
|
485
517
|
pub fn all_diagnostics(&self) -> Vec<&Diagnostic> {
|
|
486
|
-
|
|
487
|
-
let declaration_diagnostics = self.declarations.values().flat_map(Declaration::diagnostics);
|
|
488
|
-
|
|
489
|
-
document_diagnostics.chain(declaration_diagnostics).collect()
|
|
518
|
+
self.documents.values().flat_map(Document::diagnostics).collect()
|
|
490
519
|
}
|
|
491
520
|
|
|
492
521
|
/// Interns a string in the graph unless already interned. This method is only used to back the
|
|
@@ -1198,9 +1227,6 @@ impl Graph {
|
|
|
1198
1227
|
for def_id in detach_def_ids {
|
|
1199
1228
|
decl.remove_definition(def_id);
|
|
1200
1229
|
}
|
|
1201
|
-
if !detach_def_ids.is_empty() {
|
|
1202
|
-
decl.clear_diagnostics();
|
|
1203
|
-
}
|
|
1204
1230
|
}
|
|
1205
1231
|
|
|
1206
1232
|
let Some(decl) = self.declarations.get(&decl_id) else {
|
|
@@ -1540,8 +1566,8 @@ mod tests {
|
|
|
1540
1566
|
use crate::model::declaration::Ancestors;
|
|
1541
1567
|
use crate::test_utils::GraphTest;
|
|
1542
1568
|
use crate::{
|
|
1543
|
-
assert_declaration_does_not_exist, assert_dependents, assert_descendants,
|
|
1544
|
-
assert_no_diagnostics, assert_no_members,
|
|
1569
|
+
assert_declaration_does_not_exist, assert_declaration_kind_eq, assert_dependents, assert_descendants,
|
|
1570
|
+
assert_members_eq, assert_no_diagnostics, assert_no_members,
|
|
1545
1571
|
};
|
|
1546
1572
|
|
|
1547
1573
|
#[test]
|
|
@@ -1563,6 +1589,41 @@ mod tests {
|
|
|
1563
1589
|
);
|
|
1564
1590
|
}
|
|
1565
1591
|
|
|
1592
|
+
#[test]
|
|
1593
|
+
fn singleton_method_in_non_namespace_owner_does_not_panic() {
|
|
1594
|
+
// `Aliased = Bar` assigns a constant to another constant, producing a (non-promotable)
|
|
1595
|
+
// `ConstantAlias` declaration. Reopening it with `class Aliased` is valid Ruby (it reopens
|
|
1596
|
+
// `Bar`), but the class definition is attached to the existing `ConstantAlias` declaration
|
|
1597
|
+
// without promoting it to a namespace. A `def self.foo` inside then has a `SelfReceiver`
|
|
1598
|
+
// owner whose declaration is not a namespace. Resolving that definition to its declaration
|
|
1599
|
+
// must return `None` rather than panicking.
|
|
1600
|
+
let mut context = GraphTest::new();
|
|
1601
|
+
|
|
1602
|
+
context.index_uri(
|
|
1603
|
+
"file:///foo.rb",
|
|
1604
|
+
"
|
|
1605
|
+
class Bar
|
|
1606
|
+
end
|
|
1607
|
+
|
|
1608
|
+
Aliased = Bar
|
|
1609
|
+
|
|
1610
|
+
class Aliased
|
|
1611
|
+
def self.foo; end
|
|
1612
|
+
end
|
|
1613
|
+
",
|
|
1614
|
+
);
|
|
1615
|
+
context.resolve();
|
|
1616
|
+
|
|
1617
|
+
// The declaration stays a non-namespace constant alias.
|
|
1618
|
+
assert_declaration_kind_eq!(context, "Aliased", "ConstantAlias");
|
|
1619
|
+
|
|
1620
|
+
// Mirrors what consumers like the DOT exporter do: resolve every definition to its
|
|
1621
|
+
// declaration. This previously panicked on the singleton method's non-namespace owner.
|
|
1622
|
+
for definition in context.graph().definitions().values() {
|
|
1623
|
+
let _ = context.graph().definition_to_declaration_id(definition);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1566
1627
|
#[test]
|
|
1567
1628
|
fn deleting_file_triggers_name_dependent_cleanup() {
|
|
1568
1629
|
let mut context = GraphTest::new();
|
|
@@ -6,10 +6,13 @@ use std::{
|
|
|
6
6
|
hash::{BuildHasher, Hasher},
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
use crate::assert_mem_size;
|
|
10
|
+
|
|
9
11
|
#[derive(Default)]
|
|
10
12
|
pub struct IdentityHasher {
|
|
11
13
|
hash: u64,
|
|
12
14
|
}
|
|
15
|
+
assert_mem_size!(IdentityHasher, 8);
|
|
13
16
|
|
|
14
17
|
impl Hasher for IdentityHasher {
|
|
15
18
|
fn write(&mut self, _bytes: &[u8]) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
use crate::assert_mem_size;
|
|
2
|
+
|
|
1
3
|
/// A Ruby keyword with its documentation.
|
|
2
4
|
#[derive(Debug)]
|
|
3
5
|
pub struct Keyword {
|
|
@@ -6,6 +8,7 @@ pub struct Keyword {
|
|
|
6
8
|
/// Documentation string for hover display
|
|
7
9
|
documentation: &'static str,
|
|
8
10
|
}
|
|
11
|
+
assert_mem_size!(Keyword, 32);
|
|
9
12
|
|
|
10
13
|
impl Keyword {
|
|
11
14
|
#[must_use]
|
|
@@ -141,6 +141,7 @@ pub struct ResolvedName {
|
|
|
141
141
|
name: Name,
|
|
142
142
|
declaration_id: DeclarationId,
|
|
143
143
|
}
|
|
144
|
+
assert_mem_size!(ResolvedName, 48);
|
|
144
145
|
|
|
145
146
|
impl ResolvedName {
|
|
146
147
|
#[must_use]
|
|
@@ -173,6 +174,7 @@ pub enum NameRef {
|
|
|
173
174
|
/// This name has been resolved to an existing declaration
|
|
174
175
|
Resolved(Box<ResolvedName>),
|
|
175
176
|
}
|
|
177
|
+
assert_mem_size!(NameRef, 16);
|
|
176
178
|
|
|
177
179
|
impl NameRef {
|
|
178
180
|
#[must_use]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use crate::assert_mem_size;
|
|
1
2
|
use std::ops::Deref;
|
|
2
3
|
|
|
3
4
|
/// A reference-counted string used in the graph.
|
|
@@ -11,6 +12,7 @@ pub struct StringRef {
|
|
|
11
12
|
value: String,
|
|
12
13
|
ref_count: u32,
|
|
13
14
|
}
|
|
15
|
+
assert_mem_size!(StringRef, 32);
|
|
14
16
|
|
|
15
17
|
impl StringRef {
|
|
16
18
|
#[must_use]
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
use core::fmt;
|
|
2
2
|
use std::fmt::Display;
|
|
3
3
|
|
|
4
|
+
use crate::assert_mem_size;
|
|
5
|
+
|
|
4
6
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
5
7
|
pub enum Visibility {
|
|
6
8
|
Public,
|
|
@@ -8,6 +10,7 @@ pub enum Visibility {
|
|
|
8
10
|
Private,
|
|
9
11
|
ModuleFunction,
|
|
10
12
|
}
|
|
13
|
+
assert_mem_size!(Visibility, 1);
|
|
11
14
|
|
|
12
15
|
impl Visibility {
|
|
13
16
|
/// Parse a visibility from a string.
|
|
@@ -1524,6 +1524,7 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
1524
1524
|
let name = Self::location_to_string(&node.name_loc());
|
|
1525
1525
|
let str_id = self.intern_string(format!("{name}()"));
|
|
1526
1526
|
let offset = Offset::from_prism_location(&node.location());
|
|
1527
|
+
let name_offset = Offset::from_prism_location(&node.name_loc());
|
|
1527
1528
|
let parameters = self.collect_parameters(node);
|
|
1528
1529
|
let is_singleton = node.receiver().is_some();
|
|
1529
1530
|
|
|
@@ -1586,6 +1587,7 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
1586
1587
|
str_id,
|
|
1587
1588
|
uri_id: self.uri_id,
|
|
1588
1589
|
offset: offset.clone(),
|
|
1590
|
+
name_offset: name_offset.clone(),
|
|
1589
1591
|
comments: comments.clone(),
|
|
1590
1592
|
flags: flags.clone(),
|
|
1591
1593
|
signatures: Signatures::Simple(parameters.clone().into_boxed_slice()),
|
|
@@ -1604,6 +1606,7 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
1604
1606
|
str_id,
|
|
1605
1607
|
uri_id: self.uri_id,
|
|
1606
1608
|
offset: offset.clone(),
|
|
1609
|
+
name_offset: name_offset.clone(),
|
|
1607
1610
|
comments,
|
|
1608
1611
|
flags,
|
|
1609
1612
|
signatures: Signatures::Simple(parameters.into_boxed_slice()),
|
|
@@ -1638,6 +1641,7 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
1638
1641
|
str_id,
|
|
1639
1642
|
uri_id: self.uri_id,
|
|
1640
1643
|
offset: offset.clone(),
|
|
1644
|
+
name_offset,
|
|
1641
1645
|
comments,
|
|
1642
1646
|
flags,
|
|
1643
1647
|
signatures: Signatures::Simple(parameters.into_boxed_slice()),
|
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -454,20 +454,7 @@ fn expression_completion<'a>(
|
|
|
454
454
|
let NameRef::Resolved(name_ref) = name_ref else {
|
|
455
455
|
return Err(format!("Expected name {nesting_name_id} to be resolved").into());
|
|
456
456
|
};
|
|
457
|
-
|
|
458
|
-
// When explicit, follow constant aliases so callers can pass whatever the expression that set self
|
|
459
|
-
// resolves to without having to unwrap aliases themselves. Missing or non-namespace decls are graph
|
|
460
|
-
// inconsistencies and surfaced as errors.
|
|
461
|
-
let resolved_self_decl_id = match self_decl_id {
|
|
462
|
-
Some(id) => resolve_self_namespace(graph, id)?,
|
|
463
|
-
None => *name_ref.declaration_id(),
|
|
464
|
-
};
|
|
465
|
-
let self_decl = graph
|
|
466
|
-
.declarations()
|
|
467
|
-
.get(&resolved_self_decl_id)
|
|
468
|
-
.unwrap()
|
|
469
|
-
.as_namespace()
|
|
470
|
-
.ok_or("Expected associated declaration to be a namespace")?;
|
|
457
|
+
|
|
471
458
|
let innermost_lexical_decl = graph
|
|
472
459
|
.declarations()
|
|
473
460
|
.get(name_ref.declaration_id())
|
|
@@ -496,7 +483,16 @@ fn expression_completion<'a>(
|
|
|
496
483
|
|
|
497
484
|
// Collect methods and instance variables, which are based on the inheritance chain of the `self` type (which may
|
|
498
485
|
// not match the immediate lexical scope)
|
|
499
|
-
|
|
486
|
+
if let Some(self_decl_id) = self_decl_id.map(|id| resolve_self_namespace(graph, id)).transpose()? {
|
|
487
|
+
let self_decl = graph
|
|
488
|
+
.declarations()
|
|
489
|
+
.get(&self_decl_id)
|
|
490
|
+
.unwrap()
|
|
491
|
+
.as_namespace()
|
|
492
|
+
.ok_or("Expected associated declaration to be a namespace")?;
|
|
493
|
+
|
|
494
|
+
collect_methods_and_ivars_from_self(graph, self_decl, &mut context, &mut candidates);
|
|
495
|
+
}
|
|
500
496
|
|
|
501
497
|
// Keywords are always available in expression contexts
|
|
502
498
|
candidates.extend(keywords::KEYWORDS.iter().map(CompletionCandidate::Keyword));
|
|
@@ -1120,7 +1116,7 @@ mod tests {
|
|
|
1120
1116
|
assert_declaration_completion_eq!(
|
|
1121
1117
|
context,
|
|
1122
1118
|
CompletionReceiver::Expression {
|
|
1123
|
-
self_decl_id:
|
|
1119
|
+
self_decl_id: Some(DeclarationId::from("Child")),
|
|
1124
1120
|
nesting_name_id: name_id,
|
|
1125
1121
|
},
|
|
1126
1122
|
[
|
|
@@ -1169,7 +1165,7 @@ mod tests {
|
|
|
1169
1165
|
assert_declaration_completion_eq!(
|
|
1170
1166
|
context,
|
|
1171
1167
|
CompletionReceiver::Expression {
|
|
1172
|
-
self_decl_id:
|
|
1168
|
+
self_decl_id: Some(DeclarationId::from("Child")),
|
|
1173
1169
|
nesting_name_id: name_id,
|
|
1174
1170
|
},
|
|
1175
1171
|
[
|
|
@@ -1218,7 +1214,7 @@ mod tests {
|
|
|
1218
1214
|
assert_declaration_completion_eq!(
|
|
1219
1215
|
context,
|
|
1220
1216
|
CompletionReceiver::Expression {
|
|
1221
|
-
self_decl_id:
|
|
1217
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
1222
1218
|
nesting_name_id: name_id,
|
|
1223
1219
|
},
|
|
1224
1220
|
[
|
|
@@ -1268,7 +1264,7 @@ mod tests {
|
|
|
1268
1264
|
assert_declaration_completion_eq!(
|
|
1269
1265
|
context,
|
|
1270
1266
|
CompletionReceiver::Expression {
|
|
1271
|
-
self_decl_id:
|
|
1267
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>")),
|
|
1272
1268
|
nesting_name_id: name_id,
|
|
1273
1269
|
},
|
|
1274
1270
|
[
|
|
@@ -1288,7 +1284,7 @@ mod tests {
|
|
|
1288
1284
|
assert_declaration_completion_eq!(
|
|
1289
1285
|
context,
|
|
1290
1286
|
CompletionReceiver::Expression {
|
|
1291
|
-
self_decl_id:
|
|
1287
|
+
self_decl_id: Some(DeclarationId::from("Bar")),
|
|
1292
1288
|
nesting_name_id: name_id,
|
|
1293
1289
|
},
|
|
1294
1290
|
[
|
|
@@ -1305,6 +1301,48 @@ mod tests {
|
|
|
1305
1301
|
);
|
|
1306
1302
|
}
|
|
1307
1303
|
|
|
1304
|
+
#[test]
|
|
1305
|
+
fn completion_candidates_for_instance_variables_inside_singleton_class_body() {
|
|
1306
|
+
let mut context = GraphTest::new();
|
|
1307
|
+
context.index_uri(
|
|
1308
|
+
"file:///foo.rb",
|
|
1309
|
+
"
|
|
1310
|
+
class Foo
|
|
1311
|
+
@class_level_ivar = 1
|
|
1312
|
+
|
|
1313
|
+
def initialize
|
|
1314
|
+
@instance_level_ivar = 1
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
class << self
|
|
1318
|
+
@singleton_level_ivar = 1
|
|
1319
|
+
end
|
|
1320
|
+
end
|
|
1321
|
+
",
|
|
1322
|
+
);
|
|
1323
|
+
context.resolve();
|
|
1324
|
+
|
|
1325
|
+
let foo_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
1326
|
+
let name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_id), Some(foo_id)).id();
|
|
1327
|
+
|
|
1328
|
+
assert_declaration_completion_eq!(
|
|
1329
|
+
context,
|
|
1330
|
+
CompletionReceiver::Expression {
|
|
1331
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>::<<Foo>>")),
|
|
1332
|
+
nesting_name_id: name_id,
|
|
1333
|
+
},
|
|
1334
|
+
[
|
|
1335
|
+
"Module",
|
|
1336
|
+
"Class",
|
|
1337
|
+
"Object",
|
|
1338
|
+
"BasicObject",
|
|
1339
|
+
"Kernel",
|
|
1340
|
+
"Foo",
|
|
1341
|
+
"Foo::<Foo>::<<Foo>>#@singleton_level_ivar"
|
|
1342
|
+
]
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1308
1346
|
#[test]
|
|
1309
1347
|
fn completion_candidates_includes_constants_accessible_within_lexical_scope() {
|
|
1310
1348
|
let mut context = GraphTest::new();
|
|
@@ -1337,10 +1375,11 @@ mod tests {
|
|
|
1337
1375
|
Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
|
|
1338
1376
|
)
|
|
1339
1377
|
.id();
|
|
1378
|
+
|
|
1340
1379
|
assert_declaration_completion_eq!(
|
|
1341
1380
|
context,
|
|
1342
1381
|
CompletionReceiver::Expression {
|
|
1343
|
-
self_decl_id:
|
|
1382
|
+
self_decl_id: Some(DeclarationId::from("Bar")),
|
|
1344
1383
|
nesting_name_id: name_id,
|
|
1345
1384
|
},
|
|
1346
1385
|
[
|
|
@@ -1361,7 +1400,7 @@ mod tests {
|
|
|
1361
1400
|
assert_declaration_completion_eq!(
|
|
1362
1401
|
context,
|
|
1363
1402
|
CompletionReceiver::Expression {
|
|
1364
|
-
self_decl_id:
|
|
1403
|
+
self_decl_id: Some(DeclarationId::from("Bar")),
|
|
1365
1404
|
nesting_name_id: name_id,
|
|
1366
1405
|
},
|
|
1367
1406
|
[
|
|
@@ -1405,7 +1444,7 @@ mod tests {
|
|
|
1405
1444
|
assert_declaration_completion_eq!(
|
|
1406
1445
|
context,
|
|
1407
1446
|
CompletionReceiver::Expression {
|
|
1408
|
-
self_decl_id:
|
|
1447
|
+
self_decl_id: Some(DeclarationId::from("Foo::Bar")),
|
|
1409
1448
|
nesting_name_id: name_id,
|
|
1410
1449
|
},
|
|
1411
1450
|
[
|
|
@@ -1452,7 +1491,7 @@ mod tests {
|
|
|
1452
1491
|
assert_declaration_completion_eq!(
|
|
1453
1492
|
context,
|
|
1454
1493
|
CompletionReceiver::Expression {
|
|
1455
|
-
self_decl_id:
|
|
1494
|
+
self_decl_id: Some(DeclarationId::from("Foo::Bar")),
|
|
1456
1495
|
nesting_name_id: name_id,
|
|
1457
1496
|
},
|
|
1458
1497
|
["Foo::Bar", "$var2", "$var", "Foo::Bar#bar_m()"]
|
|
@@ -1982,7 +2021,7 @@ mod tests {
|
|
|
1982
2021
|
assert_declaration_completion_eq!(
|
|
1983
2022
|
context,
|
|
1984
2023
|
CompletionReceiver::MethodArgument {
|
|
1985
|
-
self_decl_id:
|
|
2024
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
1986
2025
|
nesting_name_id: name_id,
|
|
1987
2026
|
method_decl_id: DeclarationId::from("Foo#greet()"),
|
|
1988
2027
|
},
|
|
@@ -2000,6 +2039,48 @@ mod tests {
|
|
|
2000
2039
|
);
|
|
2001
2040
|
}
|
|
2002
2041
|
|
|
2042
|
+
#[test]
|
|
2043
|
+
fn method_argument_in_body_completion_uses_singleton_self() {
|
|
2044
|
+
let mut context = GraphTest::new();
|
|
2045
|
+
context.index_uri(
|
|
2046
|
+
"file:///foo.rb",
|
|
2047
|
+
"
|
|
2048
|
+
class Foo
|
|
2049
|
+
@class_level_ivar = 1
|
|
2050
|
+
|
|
2051
|
+
def instance_method; end
|
|
2052
|
+
|
|
2053
|
+
def self.configure(name:, label: 'default'); end
|
|
2054
|
+
|
|
2055
|
+
# `configure(...)` is invoked at class body level — cursor inside the args.
|
|
2056
|
+
end
|
|
2057
|
+
",
|
|
2058
|
+
);
|
|
2059
|
+
context.resolve();
|
|
2060
|
+
|
|
2061
|
+
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2062
|
+
assert_declaration_completion_eq!(
|
|
2063
|
+
context,
|
|
2064
|
+
CompletionReceiver::MethodArgument {
|
|
2065
|
+
self_decl_id: Some(DeclarationId::from("Foo::<Foo>")),
|
|
2066
|
+
nesting_name_id: name_id,
|
|
2067
|
+
method_decl_id: DeclarationId::from("Foo::<Foo>#configure()"),
|
|
2068
|
+
},
|
|
2069
|
+
[
|
|
2070
|
+
"Module",
|
|
2071
|
+
"Class",
|
|
2072
|
+
"Object",
|
|
2073
|
+
"BasicObject",
|
|
2074
|
+
"Kernel",
|
|
2075
|
+
"Foo",
|
|
2076
|
+
"Foo::<Foo>#configure()",
|
|
2077
|
+
"Foo::<Foo>#@class_level_ivar",
|
|
2078
|
+
"name:",
|
|
2079
|
+
"label:"
|
|
2080
|
+
]
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2003
2084
|
#[test]
|
|
2004
2085
|
fn method_argument_completion_no_keyword_params() {
|
|
2005
2086
|
let mut context = GraphTest::new();
|
|
@@ -2018,7 +2099,7 @@ mod tests {
|
|
|
2018
2099
|
assert_declaration_completion_eq!(
|
|
2019
2100
|
context,
|
|
2020
2101
|
CompletionReceiver::MethodArgument {
|
|
2021
|
-
self_decl_id:
|
|
2102
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
2022
2103
|
nesting_name_id: name_id,
|
|
2023
2104
|
method_decl_id: DeclarationId::from("Foo#bar()"),
|
|
2024
2105
|
},
|
|
@@ -2044,7 +2125,7 @@ mod tests {
|
|
|
2044
2125
|
assert_declaration_completion_eq!(
|
|
2045
2126
|
context,
|
|
2046
2127
|
CompletionReceiver::MethodArgument {
|
|
2047
|
-
self_decl_id:
|
|
2128
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
2048
2129
|
nesting_name_id: name_id,
|
|
2049
2130
|
method_decl_id: DeclarationId::from("Foo#search()"),
|
|
2050
2131
|
},
|
|
@@ -2088,7 +2169,7 @@ mod tests {
|
|
|
2088
2169
|
assert_declaration_completion_eq!(
|
|
2089
2170
|
context,
|
|
2090
2171
|
CompletionReceiver::MethodArgument {
|
|
2091
|
-
self_decl_id:
|
|
2172
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
2092
2173
|
nesting_name_id: name_id,
|
|
2093
2174
|
method_decl_id: DeclarationId::from("Foo#bar()"),
|
|
2094
2175
|
},
|
|
@@ -2181,7 +2262,7 @@ mod tests {
|
|
|
2181
2262
|
assert_completion_eq!(
|
|
2182
2263
|
context,
|
|
2183
2264
|
CompletionReceiver::MethodArgument {
|
|
2184
|
-
self_decl_id:
|
|
2265
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
2185
2266
|
nesting_name_id: name_id,
|
|
2186
2267
|
method_decl_id: DeclarationId::from("Foo#bar()"),
|
|
2187
2268
|
},
|
|
@@ -2612,7 +2693,7 @@ mod tests {
|
|
|
2612
2693
|
assert_declaration_completion_eq!(
|
|
2613
2694
|
context,
|
|
2614
2695
|
CompletionReceiver::Expression {
|
|
2615
|
-
self_decl_id:
|
|
2696
|
+
self_decl_id: Some(DeclarationId::from("Object")),
|
|
2616
2697
|
nesting_name_id: name_id,
|
|
2617
2698
|
},
|
|
2618
2699
|
[
|
|
@@ -2950,7 +3031,7 @@ mod tests {
|
|
|
2950
3031
|
assert_declaration_completion_eq!(
|
|
2951
3032
|
context,
|
|
2952
3033
|
CompletionReceiver::Expression {
|
|
2953
|
-
self_decl_id:
|
|
3034
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
2954
3035
|
nesting_name_id: foo_name_id,
|
|
2955
3036
|
},
|
|
2956
3037
|
[
|
|
@@ -2970,7 +3051,7 @@ mod tests {
|
|
|
2970
3051
|
assert_declaration_completion_eq!(
|
|
2971
3052
|
context,
|
|
2972
3053
|
CompletionReceiver::Expression {
|
|
2973
|
-
self_decl_id:
|
|
3054
|
+
self_decl_id: Some(DeclarationId::from("Bar")),
|
|
2974
3055
|
nesting_name_id: bar_name_id,
|
|
2975
3056
|
},
|
|
2976
3057
|
[
|
|
@@ -3286,7 +3367,7 @@ mod tests {
|
|
|
3286
3367
|
assert_declaration_completion_eq!(
|
|
3287
3368
|
context,
|
|
3288
3369
|
CompletionReceiver::Expression {
|
|
3289
|
-
self_decl_id:
|
|
3370
|
+
self_decl_id: Some(DeclarationId::from("Foo")),
|
|
3290
3371
|
nesting_name_id: foo_name_id,
|
|
3291
3372
|
},
|
|
3292
3373
|
[
|