// Copyright (C) 2011, 2012 Strahinja Val Markovic // // This file is part of YouCompleteMe. // // YouCompleteMe is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // YouCompleteMe is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with YouCompleteMe. If not, see . #include "Result.h" #include "standard.h" #include "Utils.h" #include #include using boost::algorithm::istarts_with; namespace YouCompleteMe { namespace { int LongestCommonSubsequenceLength( const std::string &first, const std::string &second ) { const std::string &longer = first.size() > second.size() ? first : second; const std::string &shorter = first.size() > second.size() ? second : first; int longer_len = longer.size(); int shorter_len = shorter.size(); std::vector previous( shorter_len + 1, 0 ); std::vector current( shorter_len + 1, 0 ); for (int i = 0; i < longer_len; ++i ) { for (int j = 0; j < shorter_len; ++j ) { if ( toupper( longer[ i ] ) == toupper( shorter[ j ] ) ) current[ j + 1 ] = previous[ j ] + 1; else current[ j + 1 ] = std::max( current[ j ], previous[ j + 1 ] ); } for (int j = 0; j < shorter_len; ++j ) { previous[ j + 1 ] = current[ j + 1 ]; } } return current[ shorter_len ]; } int NumWordBoundaryCharMatches( const std::string &query, const std::string &word_boundary_chars ) { return LongestCommonSubsequenceLength(query, word_boundary_chars); } } // unnamed namespace Result::Result( bool is_subsequence ) : is_subsequence_( is_subsequence ), first_char_same_in_query_and_text_( false ), ratio_of_word_boundary_chars_in_query_( 0 ), word_boundary_char_utilization_( 0 ), query_is_candidate_prefix_( false ), text_is_lowercase_( false ), char_match_index_sum_( 0 ), text_( NULL ) { } Result::Result( bool is_subsequence, const std::string *text, bool text_is_lowercase, int char_match_index_sum, const std::string &word_boundary_chars, const std::string &query ) : is_subsequence_( is_subsequence ), first_char_same_in_query_and_text_( false ), ratio_of_word_boundary_chars_in_query_( 0 ), word_boundary_char_utilization_( 0 ), query_is_candidate_prefix_( false ), text_is_lowercase_( text_is_lowercase ), char_match_index_sum_( char_match_index_sum ), text_( text ) { if ( is_subsequence ) SetResultFeaturesFromQuery( word_boundary_chars, query ); } // TODO: do we need a custom copy ctor? Result::Result( const Result& other ) : is_subsequence_( other.is_subsequence_ ), first_char_same_in_query_and_text_( other.first_char_same_in_query_and_text_ ), ratio_of_word_boundary_chars_in_query_( other.ratio_of_word_boundary_chars_in_query_ ), word_boundary_char_utilization_( other.word_boundary_char_utilization_ ), query_is_candidate_prefix_( other.query_is_candidate_prefix_ ), text_is_lowercase_( other.text_is_lowercase_ ), char_match_index_sum_( other.char_match_index_sum_ ), text_( other.text_ ) { } bool Result::operator< ( const Result &other ) const { // Yes, this is ugly but it also needs to be fast. Since this is called a // bazillion times, we have to make sure only the required comparisons are // made, and no more. if ( first_char_same_in_query_and_text_ != other.first_char_same_in_query_and_text_ ) { return first_char_same_in_query_and_text_; } bool equal_wb_ratios = AlmostEqual( ratio_of_word_boundary_chars_in_query_, other.ratio_of_word_boundary_chars_in_query_ ); bool equal_wb_utilization = AlmostEqual( word_boundary_char_utilization_, other.word_boundary_char_utilization_ ); if ( AlmostEqual( ratio_of_word_boundary_chars_in_query_, 1.0 ) || AlmostEqual( other.ratio_of_word_boundary_chars_in_query_, 1.0 ) ) { if ( !equal_wb_ratios ) { return ratio_of_word_boundary_chars_in_query_ > other.ratio_of_word_boundary_chars_in_query_; } else { if ( !equal_wb_utilization ) return word_boundary_char_utilization_ > other.word_boundary_char_utilization_; } } if ( query_is_candidate_prefix_ != other.query_is_candidate_prefix_ ) return query_is_candidate_prefix_; if ( !equal_wb_ratios ) { return ratio_of_word_boundary_chars_in_query_ > other.ratio_of_word_boundary_chars_in_query_; } else { if ( !equal_wb_utilization ) return word_boundary_char_utilization_ > other.word_boundary_char_utilization_; } if ( char_match_index_sum_ != other.char_match_index_sum_ ) return char_match_index_sum_ < other.char_match_index_sum_; if ( text_->length() != other.text_->length() ) return text_->length() < other.text_->length(); if ( text_is_lowercase_ != other.text_is_lowercase_ ) return text_is_lowercase_; return *text_ < *other.text_; } void Result::SetResultFeaturesFromQuery( const std::string &word_boundary_chars, const std::string &query) { if ( query.empty() || text_->empty() ) return; first_char_same_in_query_and_text_ = toupper( query[ 0 ] ) == toupper( (*text_)[ 0 ] ); int num_wb_matches = NumWordBoundaryCharMatches( query, word_boundary_chars ); ratio_of_word_boundary_chars_in_query_ = num_wb_matches / static_cast< double >( query.length() ); word_boundary_char_utilization_ = num_wb_matches / static_cast< double >( word_boundary_chars.length() ); query_is_candidate_prefix_ = istarts_with( *text_, query ); } } // namespace YouCompleteMe