class Rubydex::Graph
The global graph representing all declarations and their relationships for the workspace
Note: this class is partially defined in C to integrate with the Rust backend
Constants
- INDEXABLE_EXTENSIONS
Public Class Methods
(String) → instance
Source
# File lib/rubydex/graph.rb, line 15 def configure_for_workspace(workspace_path) graph = new graph.load_config(Config.load(workspace_path)) graph end
Creates a new graph with the loaded configuration. For use cases where the graph must be shared between different tools, do not use this. Create and own a Config object instead.
Public Instance Methods
static VALUE rdxr_graph_aref(VALUE self, VALUE key) {
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
if (TYPE(key) != T_STRING) {
rb_raise(rb_eTypeError, "expected String");
}
const CDeclaration *decl = rdx_graph_get_declaration(graph, StringValueCStr(key));
if (decl == NULL) {
return Qnil;
}
VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
VALUE argv[] = {self, ULL2NUM(decl->id)};
free_c_declaration(decl);
return rb_class_new_instance(2, argv, decl_class);
}
Returns the declaration for the fully qualified name, or nil when no declaration exists.
static VALUE rdxr_graph_check_integrity(VALUE self) {
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t error_count = 0;
const char *const *errors = rdx_check_integrity(graph, &error_count);
if (errors == NULL) {
return rb_ary_new();
}
VALUE cIntegrityError = rb_const_get(mRubydex, rb_intern("IntegrityFailure"));
VALUE array = rb_ary_new_capa((long)error_count);
for (size_t i = 0; i < error_count; i++) {
VALUE argv[] = {rb_utf8_str_new_cstr(errors[i])};
VALUE error = rb_class_new_instance(1, argv, cIntegrityError);
rb_ary_push(array, error);
}
free_c_string_array(errors, error_count);
return array;
}
Returns an array of integrity failures, or an empty array if no issues were found.
Source
static VALUE rdxr_graph_complete_expression(int argc, VALUE *argv, VALUE self) {
VALUE nesting, opts;
rb_scan_args(argc, argv, "1:", &nesting, &opts);
rdxi_check_array_of_strings(nesting);
const char *self_receiver = extract_self_receiver(opts);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t nesting_count = RARRAY_LEN(nesting);
char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
struct CompletionResult result =
rdx_graph_complete_expression(graph, (const char *const *)converted_nesting, nesting_count, self_receiver);
rdxi_free_str_array(converted_nesting, nesting_count);
return completion_result_to_ruby_array(result, self);
}
Returns completion candidates for an expression context. The nesting array represents the lexical scope stack. The required self_receiver keyword argument overrides the self type; pass nil when the self type is unknown.
static VALUE rdxr_graph_complete_method_argument(int argc, VALUE *argv, VALUE self) {
VALUE name, nesting, opts;
rb_scan_args(argc, argv, "2:", &name, &nesting, &opts);
Check_Type(name, T_STRING);
rdxi_check_array_of_strings(nesting);
const char *name_string = StringValueCStr(name);
const char *self_receiver = extract_self_receiver(opts);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t nesting_count = RARRAY_LEN(nesting);
char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
struct CompletionResult result = rdx_graph_complete_method_argument(
graph, name_string, (const char *const *)converted_nesting, nesting_count, self_receiver);
rdxi_free_str_array(converted_nesting, nesting_count);
return completion_result_to_ruby_array(result, self);
}
Returns completion candidates inside a method call’s argument list. See complete_expression for self_receiver semantics.
static VALUE rdxr_graph_complete_method_call(int argc, VALUE *argv, VALUE self) {
VALUE name, opts;
rb_scan_args(argc, argv, "1:", &name, &opts);
Check_Type(name, T_STRING);
const char *self_receiver = extract_self_receiver(opts);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
struct CompletionResult result =
rdx_graph_complete_method_call(graph, StringValueCStr(name), self_receiver);
return completion_result_to_ruby_array(result, self);
}
Returns completion candidates after a method call operator such as foo. The required self_receiver keyword argument is the caller’s runtime self type; pass nil when there is no caller context.
static VALUE rdxr_graph_complete_namespace_access(int argc, VALUE *argv, VALUE self) {
VALUE name, opts;
rb_scan_args(argc, argv, "1:", &name, &opts);
Check_Type(name, T_STRING);
const char *self_receiver = extract_self_receiver(opts);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
struct CompletionResult result =
rdx_graph_complete_namespace_access(graph, StringValueCStr(name), self_receiver);
return completion_result_to_ruby_array(result, self);
}
Returns completion candidates after a namespace access operator such as Foo::. The required self_receiver keyword argument is the caller’s runtime self type; pass nil when there is no caller context.
static VALUE rdxr_graph_constant_references(VALUE self) {
if (!rb_block_given_p()) {
return rb_enumeratorize_with_size(self, rb_str_new2("constant_references"), 0, NULL,
graph_constant_references_size);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
void *iter = rdx_graph_constant_references_iter_new(graph);
VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
rb_ensure(rdxi_constant_references_yield, args, rdxi_constant_references_ensure, args);
return self;
}
Returns an enumerator that yields constant references lazily.
static VALUE rdxr_graph_dead_code_candidates(VALUE self) {
if (!rb_block_given_p()) {
return rb_enumeratorize(self, rb_str_new2("dead_code_candidates"), 0, NULL);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
void *iter = rdx_graph_dead_code_candidates(graph);
VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
rb_ensure(rdxi_declarations_yield, args, rdxi_declarations_ensure, args);
return self;
}
Returns an enumerator over declarations that may be unused because the analysis could not find any references to them. This misses metaprogramming or untyped code, which is why the term candidates is used.
Currently, only supports constants.
static VALUE rdxr_graph_declarations(VALUE self) {
if (!rb_block_given_p()) {
return rb_enumeratorize_with_size(self, rb_str_new2("declarations"), 0, NULL, graph_declarations_size);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
void *iter = rdx_graph_declarations_iter_new(graph);
VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
rb_ensure(rdxi_declarations_yield, args, rdxi_declarations_ensure, args);
return self;
}
Returns an enumerator that yields all declarations lazily.
static VALUE rdxr_graph_delete_document(VALUE self, VALUE uri) {
Check_Type(uri, T_STRING);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
const uint64_t *uri_id = rdx_graph_delete_document(graph, StringValueCStr(uri));
if (uri_id == NULL) {
return Qnil;
}
VALUE argv[] = {self, ULL2NUM(*uri_id)};
free_u64(uri_id);
return rb_class_new_instance(2, argv, cDocument);
}
Deletes a document and all of its definitions from the graph. Returns the removed document, or nil if it does not exist.
static VALUE rdxr_graph_diagnostics(VALUE self) {
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
DiagnosticArray *array = rdx_graph_diagnostics(graph);
if (array == NULL || array->len == 0) {
if (array != NULL) {
rdx_diagnostics_free(array);
}
return rb_ary_new();
}
VALUE diagnostics = rb_ary_new_capa((long)array->len);
for (size_t i = 0; i < array->len; i++) {
DiagnosticEntry entry = array->items[i];
VALUE message = entry.message == NULL ? Qnil : rb_utf8_str_new_cstr(entry.message);
VALUE rule = rdxi_rule_class_from_name(entry.rule.name, entry.rule.name_length);
VALUE location = rdxi_build_location_value(entry.location);
VALUE kwargs = rb_hash_new();
rb_hash_aset(kwargs, ID2SYM(rb_intern("rule")), rule);
rb_hash_aset(kwargs, ID2SYM(rb_intern("message")), message);
rb_hash_aset(kwargs, ID2SYM(rb_intern("location")), location);
VALUE diagnostic = rb_class_new_instance_kw(1, &kwargs, cDiagnostic, RB_PASS_KEYWORDS);
rb_ary_push(diagnostics, diagnostic);
}
rdx_diagnostics_free(array);
return diagnostics;
}
Returns diagnostics emitted while indexing or resolving the graph.
static VALUE rdxr_graph_document(VALUE self, VALUE uri) {
Check_Type(uri, T_STRING);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
const uint64_t *uri_id = rdx_graph_get_document(graph, StringValueCStr(uri));
if (uri_id == NULL) {
return Qnil;
}
VALUE argv[] = {self, ULL2NUM(*uri_id)};
free_u64(uri_id);
return rb_class_new_instance(2, argv, cDocument);
}
Returns the document for the URI, or nil if it does not exist.
static VALUE rdxr_graph_documents(VALUE self) {
if (!rb_block_given_p()) {
return rb_enumeratorize_with_size(self, rb_str_new2("documents"), 0, NULL, graph_documents_size);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
void *iter = rdx_graph_documents_iter_new(graph);
VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
rb_ensure(graph_documents_yield, args, graph_documents_ensure, args);
return self;
}
Returns an enumerator that yields all documents lazily.
Source
static VALUE rdxr_graph_set_encoding(VALUE self, VALUE encoding) {
Check_Type(encoding, T_STRING);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
char *encoding_string = StringValueCStr(encoding);
if (!rdx_graph_set_encoding(graph, encoding_string)) {
rb_raise(rb_eArgError, "invalid encoding `%s` (should be utf8, utf16 or utf32)", encoding_string);
}
return Qnil;
}
Sets the encoding used for transforming byte offsets into LSP code unit line and column positions.
static VALUE rdxr_graph_exclude_patterns(VALUE self, VALUE patterns) {
Check_Type(patterns, T_ARRAY);
rdxi_check_array_of_strings(patterns);
size_t length = RARRAY_LEN(patterns);
char **converted_patterns = rdxi_str_array_to_char(patterns, length);
void *graph;
TypedData_Get_Struct(self, void*, &graph_type, graph);
rdx_graph_exclude_patterns(graph, (const char **)converted_patterns, length);
rdxi_free_str_array(converted_patterns, length);
return Qnil;
}
Excludes files matching the given glob patterns from discovery during indexing.
static VALUE rdxr_graph_excluded_patterns(VALUE self) {
void *graph;
TypedData_Get_Struct(self, void*, &graph_type, graph);
size_t out_count = 0;
const char *const *results = rdx_graph_excluded_patterns(graph, &out_count);
if (results == NULL) {
return rb_ary_new();
}
VALUE array = rb_ary_new_capa((long)out_count);
for (size_t i = 0; i < out_count; i++) {
rb_ary_push(array, rb_utf8_str_new_cstr(results[i]));
}
free_c_string_array(results, out_count);
return array;
}
Returns the glob patterns currently excluded from file discovery.
static VALUE rdxr_graph_fuzzy_search(int argc, VALUE *argv, VALUE self) {
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
VALUE queries = rb_ary_new_from_values(argc, argv);
rdxi_check_array_of_strings(queries);
if (!rb_block_given_p()) {
return rb_enumeratorize(self, rb_str_new2("fuzzy_search"), argc, argv);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t length = (size_t)argc;
char **converted = rdxi_str_array_to_char(queries, length);
void *iter = rdx_graph_declarations_fuzzy_search(graph, (const char *const *)converted, length);
rdxi_free_str_array(converted, length);
return rdxr_graph_yield_search_results(self, iter);
}
Returns an enumerator that yields declarations whose name matches any of the queries fuzzily.
static VALUE rdxr_graph_index_all(VALUE self, VALUE file_paths) {
rdxi_check_array_of_strings(file_paths);
// Convert the given file paths into a char** array, so that we can pass to Rust
size_t length = RARRAY_LEN(file_paths);
char **converted_file_paths = rdxi_str_array_to_char(file_paths, length);
// Get the underlying graph pointer and then invoke the Rust index all implementation
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t error_count = 0;
const char *const *errors = rdx_index_all(graph, (const char **)converted_file_paths, length, &error_count);
rdxi_free_str_array(converted_file_paths, length);
if (errors == NULL) {
return rb_ary_new();
}
VALUE array = rb_ary_new_capa((long)error_count);
for (size_t i = 0; i < error_count; i++) {
rb_ary_push(array, rb_utf8_str_new_cstr(errors[i]));
}
free_c_string_array(errors, error_count);
return array;
}
Returns an array of I/O error messages encountered during indexing.
static VALUE rdxr_graph_index_source(VALUE self, VALUE uri, VALUE source, VALUE language_id) {
Check_Type(uri, T_STRING);
Check_Type(source, T_STRING);
Check_Type(language_id, T_STRING);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
const char *uri_str = StringValueCStr(uri);
const char *language_id_str = StringValueCStr(language_id);
const char *source_str = RSTRING_PTR(source);
size_t source_len = RSTRING_LEN(source);
enum IndexSourceResult result = rdx_index_source(graph, uri_str, source_str, source_len, language_id_str);
switch (result) {
case IndexSourceResult_Success:
break;
case IndexSourceResult_InvalidUri:
rb_raise(rb_eArgError, "invalid URI (not valid UTF-8)");
break;
case IndexSourceResult_InvalidSource:
rb_raise(rb_eArgError, "source is not valid UTF-8");
break;
case IndexSourceResult_InvalidLanguageId:
rb_raise(rb_eArgError, "invalid language_id (not valid UTF-8)");
break;
case IndexSourceResult_UnsupportedLanguageId:
rb_raise(rb_eArgError, "unsupported language_id `%s`", language_id_str);
break;
}
return Qnil;
}
Indexes a single source string in memory, dispatching to the appropriate indexer based on language_id.
→ Array[String]
Source
# File lib/rubydex/graph.rb, line 24 def index_workspace index_all(workspace_paths) end
Index all files and dependencies of the workspace that exists in workspace_path
Source
static VALUE rdxr_graph_initialize_copy(VALUE self, VALUE other) {
(void)self;
(void)other;
rb_raise(rb_eRuntimeError, "Rubydex::Graph cannot be duplicated or cloned");
}
static VALUE rdxr_graph_keyword(VALUE self, VALUE name) {
Check_Type(name, T_STRING);
const CKeyword *kw = rdx_keyword_get(StringValueCStr(name));
if (kw == NULL) {
return Qnil;
}
VALUE argv[2] = {
rb_utf8_str_new_cstr(kw->name),
rb_utf8_str_new_cstr(kw->documentation),
};
rdx_keyword_free(kw);
return rb_class_new_instance(2, argv, cKeyword);
}
Returns the keyword object for the name, or nil if it is not a Ruby keyword.
Source
static VALUE rdxr_graph_load_config(VALUE self, VALUE config_obj) {
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
void *config = rdxi_config_from_object(config_obj);
rdx_graph_load_config(graph, config);
return Qnil;
}
Applies a parsed Rubydex::Config to the graph.
static VALUE rdxr_graph_method_references(VALUE self) {
if (!rb_block_given_p()) {
return rb_enumeratorize_with_size(self, rb_str_new2("method_references"), 0, NULL,
graph_method_references_size);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
void *iter = rdx_graph_method_references_iter_new(graph);
VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
rb_ensure(rdxi_method_references_yield, args, rdxi_method_references_ensure, args);
return self;
}
Returns an enumerator that yields method references lazily.
static VALUE rdxr_graph_require_paths(VALUE self, VALUE load_path) {
rdxi_check_array_of_strings(load_path);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t paths_len = RARRAY_LEN(load_path);
char **converted_paths = rdxi_str_array_to_char(load_path, paths_len);
size_t out_count = 0;
const char *const *results = rdx_require_paths(graph, (const char **)converted_paths, paths_len, &out_count);
rdxi_free_str_array(converted_paths, paths_len);
if (results == NULL) {
return rb_ary_new();
}
VALUE array = rb_ary_new_capa((long)out_count);
for (size_t i = 0; i < out_count; i++) {
rb_ary_push(array, rb_utf8_str_new_cstr(results[i]));
}
free_c_string_array(results, out_count);
return array;
}
Returns all require paths for completion.
Source
static VALUE rdxr_graph_resolve(VALUE self) {
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
rdx_graph_resolve(graph);
return self;
}
Runs the resolver to compute declarations and ownership.
static VALUE rdxr_graph_resolve_constant(VALUE self, VALUE const_name, VALUE context) {
Check_Type(const_name, T_STRING);
const char *const_name_string = StringValueCStr(const_name);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
const CDeclaration *decl;
if (RB_TYPE_P(context, T_ARRAY)) {
rdxi_check_array_of_strings(context);
size_t length = RARRAY_LEN(context);
char **converted_names = rdxi_str_array_to_char(context, length);
decl = rdx_graph_resolve_constant(graph, const_name_string, (const char **)converted_names, length);
rdxi_free_str_array(converted_names, length);
} else if (rb_obj_is_kind_of(context, cClassDefinition) ||
rb_obj_is_kind_of(context, cSingletonClassDefinition) ||
rb_obj_is_kind_of(context, cModuleDefinition)) {
uint64_t definition_id = rdxi_definition_id_for_graph(context, self);
CDefinitionConstantResolutionResult result =
rdx_graph_resolve_constant_from_definition(graph, const_name_string, definition_id);
if (result.status == CDefinitionConstantResolution_DefinitionNotFound) {
rb_raise(rb_eRuntimeError, "Definition not found");
}
if (result.status == CDefinitionConstantResolution_InvalidDefinitionKind) {
// This is unreachable because the rb_obj_is_kind_of checks above only accept supported definition kinds.
rb_raise(rb_eRuntimeError, "Unexpected definition kind");
}
decl = result.declaration;
} else {
rb_raise(
rb_eTypeError,
"context must be an Array of Strings, ClassDefinition, SingletonClassDefinition, or ModuleDefinition"
);
}
if (decl == NULL) {
return Qnil;
}
VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
VALUE argv[] = {self, ULL2NUM(decl->id)};
free_c_declaration(decl);
return rb_class_new_instance(2, argv, decl_class);
}
Runs the resolver on a single constant reference using an array of nesting names or a namespace definition as its context.
static VALUE rdxr_graph_resolve_require_path(VALUE self, VALUE require_path, VALUE load_paths) {
Check_Type(require_path, T_STRING);
rdxi_check_array_of_strings(load_paths);
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
const char *path_str = StringValueCStr(require_path);
size_t paths_len = RARRAY_LEN(load_paths);
char **converted_paths = rdxi_str_array_to_char(load_paths, paths_len);
const uint64_t *uri_id = rdx_resolve_require_path(graph, path_str, (const char **)converted_paths, paths_len);
rdxi_free_str_array(converted_paths, paths_len);
if (uri_id == NULL) {
return Qnil;
}
VALUE argv[] = {self, ULL2NUM(*uri_id)};
free_u64(uri_id);
return rb_class_new_instance(2, argv, cDocument);
}
Resolves a require path to its document.
static VALUE rdxr_graph_search(int argc, VALUE *argv, VALUE self) {
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
VALUE queries = rb_ary_new_from_values(argc, argv);
rdxi_check_array_of_strings(queries);
if (!rb_block_given_p()) {
return rb_enumeratorize(self, rb_str_new2("search"), argc, argv);
}
void *graph;
TypedData_Get_Struct(self, void *, &graph_type, graph);
size_t length = (size_t)argc;
char **converted = rdxi_str_array_to_char(queries, length);
void *iter = rdx_graph_declarations_search(graph, (const char *const *)converted, length);
rdxi_free_str_array(converted, length);
return rdxr_graph_yield_search_results(self, iter);
}
Returns an enumerator that yields declarations whose name matches any of the queries exactly by substring.
Source
static VALUE rdxr_graph_workspace_path(VALUE self) {
void *graph;
TypedData_Get_Struct(self, void*, &graph_type, graph);
const char *result = rdx_graph_workspace_path(graph);
if (result == NULL) {
rb_raise(rb_eRuntimeError, "Converting workspace path to Ruby string failed");
}
VALUE path = rdxi_owned_c_string_to_ruby(result);
return path;
}
Returns the root directory of the workspace being indexed.
→ Array[String]
Source
# File lib/rubydex/graph.rb, line 31 def workspace_paths paths = [] root = workspace_path Dir.each_child(root) do |entry| full_path = File.join(root, entry) if File.directory?(full_path) || INDEXABLE_EXTENSIONS.include?(File.extname(entry)) paths << full_path end end add_workspace_dependency_paths(paths) add_core_rbs_definition_paths(paths) paths.uniq! paths end
Returns all workspace paths that should be indexed