Skip to content
Permalink
Browse files

code formatting

  • Loading branch information
suricactus authored and nyalldawson committed Mar 14, 2020
1 parent 4f269a5 commit 5241d64dcbacd0fa99978b90dae5f95074b28043
@@ -252,6 +252,14 @@ Tests a ``candidate`` string to see if it should be considered a match for
a specified ``search`` string.
Filter subclasses should use this method when comparing strings instead
of directly using QString.contains() or Python 'in' checks.
%End

static double fuzzyScore( const QString &candidate, const QString &search );
%Docstring
Tests a ``candidate`` string to see how likely it is a match for
a specified ``search`` string.

.. versionadded:: 3.14
%End

bool enabled() const;
@@ -48,14 +48,31 @@ void QgsLayerTreeLocatorFilter::fetchResults( const QString &string, const QgsLo
const QList<QgsLayerTreeLayer *> layers = tree->findLayers();
for ( QgsLayerTreeLayer *layer : layers )
{
if ( layer->layer() && ( stringMatches( layer->layer()->name(), string ) || ( context.usingPrefix && string.isEmpty() ) ) )
// if the layer is broken, don't include it in the results
if ( ! layer->layer() )
continue;

QgsLocatorResult result;
result.displayString = layer->layer()->name();
result.userData = layer->layerId();
result.icon = QgsMapLayerModel::iconForLayer( layer->layer() );

QgsLogger::warning( "Search for" + result.displayString + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) );
// return all the layers in case the string query is empty using an equal default score
if ( context.usingPrefix && string.isEmpty() )
{
QgsLogger::warning( "Using prefix but empty" + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) );
emit resultFetched( result );
continue;
}

result.score = fuzzyScore( result.displayString, string );
QgsLogger::warning( "scored: " + QString::number( result.score ) + QStringLiteral( __FILE__ ) + ": " + QString::number( __LINE__ ) );

if ( result.score > 0 )
{
QgsLocatorResult result;
result.displayString = layer->layer()->name();
result.userData = layer->layerId();
result.icon = QgsMapLayerModel::iconForLayer( layer->layer() );
result.score = static_cast< double >( string.length() ) / layer->layer()->name().length();
emit resultFetched( result );
continue;
}
}
}
@@ -23,6 +23,11 @@
#include "qgsmessagelog.h"


#define FUZZY_SCORE_WORD_MATCH 5
#define FUZZY_SCORE_NEW_MATCH 3
#define FUZZY_SCORE_CONSECUTIVE_MATCH 4


QgsLocatorFilter::QgsLocatorFilter( QObject *parent )
: QObject( parent )
{
@@ -44,6 +49,92 @@ bool QgsLocatorFilter::stringMatches( const QString &candidate, const QString &s
return !search.isEmpty() && candidate.contains( search, Qt::CaseInsensitive );
}

double QgsLocatorFilter::fuzzyScore( const QString &candidate, const QString &search )
{
QString candidateNormalized = candidate.simplified().normalized( QString:: NormalizationForm_C ).toLower();
QString searchNormalized = search.simplified().normalized( QString:: NormalizationForm_C ).toLower();

int candidateLength = candidateNormalized.length();
int searchLength = searchNormalized.length();
int score = 0;

// if the candidate and the search term are empty, no other option than 0 score
if ( candidateLength == 0 || searchLength == 0 )
return score;

int candidateIdx = 0;
int searchIdx = 0;
int maxScore = 0;

bool isPreviousIndexMatching = false;
bool isWordOpen = true;

// loop throught each candidate char and calculate the potential max score
while ( candidateIdx < candidateLength )
{
QChar candidateChar = candidateNormalized[ candidateIdx++ ];

// the first char is always the default score
if ( candidateIdx == 1 )
maxScore += FUZZY_SCORE_NEW_MATCH;
// every space character or end of string is a opportunity for a new word
else if ( candidateChar.isSpace() || candidateIdx == candidateLength )
maxScore += FUZZY_SCORE_WORD_MATCH;
// potentially we can match every other character
else
maxScore += FUZZY_SCORE_CONSECUTIVE_MATCH;

// we looped through all the characters
if ( searchIdx >= searchLength )
continue;

QChar searchChar = searchNormalized[ searchIdx ];

// match!
if ( candidateChar == searchChar )
{
searchIdx++;

// if we have just successfully finished a word, give higher score
if ( candidateChar.isSpace() || searchIdx == searchLength )
{
if ( isWordOpen )
score += FUZZY_SCORE_WORD_MATCH;
else if ( isPreviousIndexMatching )
score += FUZZY_SCORE_CONSECUTIVE_MATCH;
else
score += FUZZY_SCORE_NEW_MATCH;

isWordOpen = true;
}
// if we have consecutive characters matching, give higher score
else if ( isPreviousIndexMatching )
{
score += FUZZY_SCORE_CONSECUTIVE_MATCH;
}
// normal score for new independent character that matches
else
{
score += FUZZY_SCORE_NEW_MATCH;
}

isPreviousIndexMatching = true;
}
// if the current character does NOT match, we are sure we cannot build a word for now
else
{
isPreviousIndexMatching = false;
isWordOpen = false;
}
}

// we didn't loop through all the search chars, it means, that they are not present in the current candidate
if ( searchIdx != searchLength )
score = 0;

return static_cast<float>( std::max( score, 0 ) ) / std::max( maxScore, 1 );
}

bool QgsLocatorFilter::enabled() const
{
return mEnabled;
@@ -305,6 +305,13 @@ class CORE_EXPORT QgsLocatorFilter : public QObject
*/
static bool stringMatches( const QString &candidate, const QString &search );

/**
* Tests a \a candidate string to see how likely it is a match for
* a specified \a search string.
* \since 3.14
*/
static double fuzzyScore( const QString &candidate, const QString &search );

/**
* Returns TRUE if the filter is enabled.
* \see setEnabled()
@@ -103,23 +103,21 @@ void QgsLocatorModelBridge::performSearch( const QString &text )
{
setIsRunning( true );

QString textSimplified = text.simplified();

if ( mLocator->isRunning() )
{
// can't do anything while a query is running, and can't block
// here waiting for the current query to cancel
// so we queue up this string until cancel has happened
mLocator->cancelWithoutBlocking();
mNextRequestedString = textSimplified;
mNextRequestedString = text;
mHasQueuedRequest = true;
return;
}
else
{
emit resultsCleared();
mLocatorModel->deferredClear();
mLocator->fetchResults( textSimplified, createContext() );
mLocator->fetchResults( text, createContext() );
}
}

0 comments on commit 5241d64

Please sign in to comment.
You can’t perform that action at this time.