From 0906ccac68c7e123d698d906f959c73380ed14b1 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 11 Jun 2022 23:28:40 -0400 Subject: [PATCH] Use Pin::Search to query workspace symbols (#560) --- lib/solargraph/api_map.rb | 7 +++-- lib/solargraph/pin.rb | 1 + lib/solargraph/pin/search.rb | 61 ++++++++++++++++++++++++++++++++++++ lib/solargraph/source_map.rb | 11 +------ spec/pin/search_spec.rb | 34 ++++++++++++++++++++ 5 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 lib/solargraph/pin/search.rb create mode 100644 spec/pin/search_spec.rb diff --git a/lib/solargraph/api_map.rb b/lib/solargraph/api_map.rb index cbed16bb8..d39224b24 100755 --- a/lib/solargraph/api_map.rb +++ b/lib/solargraph/api_map.rb @@ -383,9 +383,10 @@ def document path # @param query [String] # @return [Array] def query_symbols query - result = [] - source_map_hash.each_value { |s| result.concat s.query_symbols(query) } - result + Pin::Search.new( + source_map_hash.values.flat_map(&:document_symbols), + query + ).results end # @param location [Solargraph::Location] diff --git a/lib/solargraph/pin.rb b/lib/solargraph/pin.rb index 2083b1939..777d564de 100644 --- a/lib/solargraph/pin.rb +++ b/lib/solargraph/pin.rb @@ -30,6 +30,7 @@ module Pin autoload :DuckMethod, 'solargraph/pin/duck_method' autoload :Singleton, 'solargraph/pin/singleton' autoload :KeywordParam, 'solargraph/pin/keyword_param' + autoload :Search, 'solargraph/pin/search' ROOT_PIN = Pin::Namespace.new(type: :class, name: '', closure: nil) end diff --git a/lib/solargraph/pin/search.rb b/lib/solargraph/pin/search.rb new file mode 100644 index 000000000..c4cb865e0 --- /dev/null +++ b/lib/solargraph/pin/search.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'jaro_winkler' + +module Solargraph + module Pin + class Search + class Result + # @return [Float] + attr_reader :match + + # @return [Pin::Base] + attr_reader :pin + + def initialize match, pin + @match = match + @pin = pin + end + end + + # @param pins [Array] + # @param query [String] + def initialize pins, query + @pins = pins + @query = query + end + + # @return [Array] + def results + @results ||= do_query + end + + private + + # @return [Array] + def do_query + return @pins if @query.nil? || @query.empty? + @pins.map do |pin| + match = [fuzzy_string_match(pin.path, @query), fuzzy_string_match(pin.name, @query)].max + Result.new(match, pin) if match > 0.6 + end + .compact + .sort do |a, b| + if a.match == b.match + a.pin.path <=> b.pin.path + else + b.match <=> a.match + end + end + .map(&:pin) + end + + # @param str1 [String] + # @param str2 [String] + # @return [Float] + def fuzzy_string_match str1, str2 + JaroWinkler.distance(str1, str2) + end + end + end +end diff --git a/lib/solargraph/source_map.rb b/lib/solargraph/source_map.rb index ad5162837..7c7ff9f6a 100644 --- a/lib/solargraph/source_map.rb +++ b/lib/solargraph/source_map.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'jaro_winkler' require 'yard' require 'yard-solargraph' require 'set' @@ -76,8 +75,7 @@ def document_symbols # @param query [String] # @return [Array] def query_symbols query - return document_symbols if query && query.empty? - document_symbols.select{ |pin| fuzzy_string_match(pin.path, query) || fuzzy_string_match(pin.name, query) } + Pin::Search.new(document_symbols, query).results end # @param position [Position] @@ -178,12 +176,5 @@ def _locate_pin line, character, *klasses # Assuming the root pin is always valid found || pins.first end - - # @param str1 [String] - # @param str2 [String] - # @return [Boolean] - def fuzzy_string_match str1, str2 - JaroWinkler.distance(str1, str2) > 0.6 - end end end diff --git a/spec/pin/search_spec.rb b/spec/pin/search_spec.rb new file mode 100644 index 000000000..9f96b4a38 --- /dev/null +++ b/spec/pin/search_spec.rb @@ -0,0 +1,34 @@ +describe Solargraph::Pin::Search do + it 'returns ordered matches on paths' do + example_class = Solargraph::Pin::Namespace.new(name: 'Example') + pins = [ + example_class, + Solargraph::Pin::Method.new(name: 'foobar', closure: example_class), + Solargraph::Pin::Method.new(name: 'foo_bar', closure: example_class) + ] + search = Solargraph::Pin::Search.new(pins, 'example') + expect(search.results.map(&:path)).to eq(['Example']) + end + + it 'returns ordered matches on names' do + example_class = Solargraph::Pin::Namespace.new(name: 'Example') + pins = [ + example_class, + Solargraph::Pin::Method.new(name: 'foobar', closure: example_class), + Solargraph::Pin::Method.new(name: 'foo_bar', closure: example_class) + ] + search = Solargraph::Pin::Search.new(pins, 'foobar') + expect(search.results.map(&:path)).to eq(['Example.foobar', 'Example.foo_bar']) + end + + it 'filters insufficient matches' do + example_class = Solargraph::Pin::Namespace.new(name: 'Example') + pins = [ + example_class, + Solargraph::Pin::Method.new(name: 'foobar', closure: example_class), + Solargraph::Pin::Method.new(name: 'bazquz', closure: example_class) + ] + search = Solargraph::Pin::Search.new(pins, 'foobar') + expect(search.results.map(&:path)).to eq(['Example.foobar']) + end +end