/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy 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.
dnSpy 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 dnSpy. If not, see .
*/
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.VisualStudio.Language.Intellisense;
namespace dnSpy.Contracts.Language.Intellisense {
struct BestMatchSelector {
enum MatchPriority {
///
/// Full match, case sensitive
///
Full,
///
/// Full acronym match, eg. TA matches TaskAwaiter.
/// This has less priority than because if there's an
/// identifier named TA, then it should be selected, and not TaskAwaiter.
///
FullAcronym,
///
/// Full match, case insensitive
///
FullIgnoringCase,
///
/// Matches start of filter, case sensitive
///
Start,
///
/// Matches acronyms at start of filter.
///
StartAcronym,
///
/// Matches start of filter, case insensitive
///
StartIgnoringCase,
///
/// Matches any location, case sensitive
///
AnyLocation,
///
/// Matches acronyms at any location.
///
AnyLocationAcronym,
///
/// Matches any location, case insensitive
///
AnyLocationIgnoringCase,
///
/// Other match
///
Other,
///
/// Not a match
///
Nothing = int.MaxValue,
}
public Completion? Result => bestCompletion;
readonly string searchText;
MatchPriority matchPriority;
Completion? bestCompletion;
readonly int[]? acronymMatchIndexes;
public BestMatchSelector(string searchText) {
this.searchText = searchText ?? throw new ArgumentNullException(nameof(searchText));
matchPriority = MatchPriority.Nothing;
bestCompletion = null;
acronymMatchIndexes = AcronymSearchHelpers.TryCreateMatchIndexes(searchText);
}
public void Select(Completion completion) {
var newMatchPriority = GetMatchPriority(completion);
if (newMatchPriority < matchPriority) {
bestCompletion = completion;
matchPriority = newMatchPriority;
}
}
int CountUpperCaseLetters(string text) {
int count = 0;
foreach (var c in text) {
if (char.IsUpper(c))
count++;
}
return count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
MatchPriority GetMatchPriority(Completion completion) {
var filterText = completion.TryGetFilterText();
Debug2.Assert(filterText is not null);
if (filterText is null)
return MatchPriority.Other;
if (filterText.Equals(searchText, StringComparison.CurrentCulture))
return MatchPriority.Full;
bool matchedAcronym = acronymMatchIndexes is not null && AcronymSearchHelpers.TryUpdateAcronymIndexes(acronymMatchIndexes, searchText, filterText);
if (matchedAcronym && CountUpperCaseLetters(filterText) == acronymMatchIndexes!.Length)
return MatchPriority.FullAcronym;
if (filterText.Equals(searchText, StringComparison.CurrentCultureIgnoreCase))
return MatchPriority.FullIgnoringCase;
int index = filterText.IndexOf(searchText, StringComparison.CurrentCulture);
if (index == 0)
return MatchPriority.Start;
if (matchedAcronym && acronymMatchIndexes![0] == 0)
return MatchPriority.StartAcronym;
int indexIgnoringCase = filterText.IndexOf(searchText, StringComparison.CurrentCultureIgnoreCase);
if (indexIgnoringCase == 0)
return MatchPriority.StartIgnoringCase;
if (index > 0)
return MatchPriority.AnyLocation;
if (matchedAcronym)
return MatchPriority.AnyLocationAcronym;
if (indexIgnoringCase > 0)
return MatchPriority.AnyLocationIgnoringCase;
return MatchPriority.Other;
}
}
}