lib/ruby_indexer/lib/ruby_indexer/index.rb in ruby-lsp-0.8.1 vs lib/ruby_indexer/lib/ruby_indexer/index.rb in ruby-lsp-0.9.0
- old
+ new
@@ -3,10 +3,13 @@
module RubyIndexer
class Index
extend T::Sig
+ # The minimum Jaro-Winkler similarity score for an entry to be considered a match for a given fuzzy search query
+ ENTRY_SIMILARITY_THRESHOLD = 0.7
+
sig { void }
def initialize
# Holds all entries in the index using the following format:
# {
# "Foo" => [#<Entry::Class>, #<Entry::Class>],
@@ -48,10 +51,25 @@
sig { params(fully_qualified_name: String).returns(T.nilable(T::Array[Entry])) }
def [](fully_qualified_name)
@entries[fully_qualified_name.delete_prefix("::")]
end
+ # Fuzzy searches index entries based on Jaro-Winkler similarity. If no query is provided, all entries are returned
+ sig { params(query: T.nilable(String)).returns(T::Array[Entry]) }
+ def fuzzy_search(query)
+ return @entries.flat_map { |_name, entries| entries } unless query
+
+ normalized_query = query.gsub("::", "").downcase
+
+ results = @entries.filter_map do |name, entries|
+ similarity = DidYouMean::JaroWinkler.distance(name.gsub("::", "").downcase, normalized_query)
+ [entries, -similarity] if similarity > ENTRY_SIMILARITY_THRESHOLD
+ end
+ results.sort_by!(&:last)
+ results.flat_map(&:first)
+ end
+
# Try to find the entry based on the nesting from the most specific to the least specific. For example, if we have
# the nesting as ["Foo", "Bar"] and the name as "Baz", we will try to find it in this order:
# 1. Foo::Bar::Baz
# 2. Foo::Baz
# 3. Baz
@@ -75,10 +93,12 @@
sig { params(path: String, source: T.nilable(String)).void }
def index_single(path, source = nil)
content = source || File.read(path)
visitor = IndexVisitor.new(self, YARP.parse(content), path)
visitor.run
+ rescue Errno::EISDIR
+ # If `path` is a directory, just ignore it and continue indexing
end
class Entry
extend T::Sig
@@ -98,9 +118,14 @@
def initialize(name, file_path, location, comments)
@name = name
@file_path = file_path
@location = location
@comments = comments
+ end
+
+ sig { returns(String) }
+ def file_name
+ File.basename(@file_path)
end
class Namespace < Entry
sig { returns(String) }
def short_name