59#include "llvm/Support/Format.h"
65static char lower(
char C) {
return C >=
'A' && C <=
'Z' ? C + (
'a' -
'A') : C; }
74 : PatN(std::min<int>(MaxPat, Pattern.size())),
75 ScoreScale(PatN ? float{1} / (
PerfectBonus * PatN) : 0), WordN(0) {
76 std::copy(Pattern.begin(), Pattern.begin() + PatN, Pat);
77 for (
int I = 0; I < PatN; ++I)
78 LowPat[I] =
lower(Pat[I]);
79 Scores[0][0][Miss] = {0, Miss};
81 for (
int P = 0; P <= PatN; ++P)
82 for (
int W = 0; W < P; ++W)
83 for (Action A : {Miss, Match})
86 llvm::MutableArrayRef(PatRole, PatN));
90 if (!(WordContainsPattern = init(Word)))
95 auto Best = std::max(Scores[PatN][WordN][Miss].Score,
96 Scores[PatN][WordN][Match].Score);
100 ScoreScale * std::min(
PerfectBonus * PatN, std::max<int>(0, Best));
112 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00,
114 0xff, 0xff, 0xff, 0xff,
115 0x55, 0x55, 0xf5, 0xff,
116 0xab, 0xaa, 0xaa, 0xaa,
117 0xaa, 0xaa, 0xea, 0xff,
118 0x57, 0x55, 0x55, 0x55,
119 0x55, 0x55, 0xd5, 0x3f,
120 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
121 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
122 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
123 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
141 0x00, 0xaa, 0xaa, 0xff,
142 0x00, 0x55, 0xaa, 0xff,
143 0x00, 0x55, 0x59, 0xff,
144 0x00, 0xaa, 0xaa, 0xff,
148template <
typename T>
static T
packedLookup(
const uint8_t *Data,
int I) {
149 return static_cast<T
>((Data[I >> 2] >> ((I & 3) * 2)) & 3);
152 llvm::MutableArrayRef<CharRole> Roles) {
153 assert(
Text.size() == Roles.size());
154 if (
Text.size() == 0)
162 auto Rotate = [&](
CharType T) { Types = ((Types << 2) | T) & 0x3f; };
163 for (
unsigned I = 0; I <
Text.size() - 1; ++I) {
166 TypeSet |= 1 <<
Type;
178bool FuzzyMatcher::init(llvm::StringRef NewWord) {
179 WordN = std::min<int>(MaxWord, NewWord.size());
182 std::copy(NewWord.begin(), NewWord.begin() + WordN, Word);
185 for (
int I = 0; I < WordN; ++I)
186 LowWord[I] =
lower(Word[I]);
189 for (
int W = 0, P = 0; P != PatN; ++W) {
192 if (LowWord[W] == LowPat[P])
200 llvm::MutableArrayRef(WordRole, WordN));
213void FuzzyMatcher::buildGraph() {
214 for (
int W = 0; W < WordN; ++W) {
215 Scores[0][W + 1][Miss] = {Scores[0][W][Miss].Score - skipPenalty(W, Miss),
219 for (
int P = 0;
P < PatN; ++
P) {
220 for (
int W = P; W < WordN; ++W) {
221 auto &Score = Scores[
P + 1][W + 1], &PreMiss = Scores[
P + 1][W];
223 auto MatchMissScore = PreMiss[Match].Score;
224 auto MissMissScore = PreMiss[Miss].Score;
226 MatchMissScore -= skipPenalty(W, Match);
227 MissMissScore -= skipPenalty(W, Miss);
229 Score[Miss] = (MatchMissScore > MissMissScore)
230 ? ScoreInfo{MatchMissScore, Match}
231 : ScoreInfo{MissMissScore, Miss};
233 auto &PreMatch = Scores[
P][W];
234 auto MatchMatchScore =
235 allowMatch(P, W, Match)
236 ? PreMatch[Match].Score + matchBonus(P, W, Match)
238 auto MissMatchScore = allowMatch(P, W, Miss)
239 ? PreMatch[Miss].Score + matchBonus(P, W, Miss)
241 Score[Match] = (MatchMatchScore > MissMatchScore)
242 ? ScoreInfo{MatchMatchScore, Match}
243 : ScoreInfo{MissMatchScore, Miss};
248bool FuzzyMatcher::allowMatch(
int P,
int W, Action Last)
const {
249 if (LowPat[P] != LowWord[W])
259 if (WordRole[W] ==
Tail &&
260 (Word[W] == LowWord[W] || !(WordTypeSet & 1 <<
Lower)))
266int FuzzyMatcher::skipPenalty(
int W, Action Last)
const {
269 if (WordRole[W] ==
Head)
277int FuzzyMatcher::matchBonus(
int P,
int W, Action Last)
const {
278 assert(LowPat[P] == LowWord[W]);
280 bool IsPatSingleCase =
281 (PatTypeSet == 1 <<
Lower) || (PatTypeSet == 1 <<
Upper);
285 if (Pat[P] == Word[W] ||
286 (WordRole[W] ==
Head && (IsPatSingleCase || PatRole[P] ==
Head)))
290 if (W == 0 || Last == Match)
293 if (WordRole[W] ==
Tail && P && Last == Miss)
296 if (PatRole[P] ==
Head && WordRole[W] ==
Tail)
299 if (P == 0 && WordRole[W] ==
Tail)
306 llvm::SmallString<256> Result;
307 OS <<
"=== Match \"" << llvm::StringRef(Word, WordN) <<
"\" against ["
308 << llvm::StringRef(Pat, PatN) <<
"] ===\n";
310 OS <<
"Pattern is empty: perfect match.\n";
311 return Result = llvm::StringRef(Word, WordN);
314 OS <<
"Word is empty: no match.\n";
317 if (!WordContainsPattern) {
318 OS <<
"Substring check failed.\n";
321 if (
isAwful(std::max(Scores[PatN][WordN][Match].Score,
322 Scores[PatN][WordN][Miss].Score))) {
323 OS <<
"Substring check passed, but all matches are forbidden\n";
325 if (!(PatTypeSet & 1 <<
Upper))
326 OS <<
"Lowercase query, so scoring ignores case\n";
332 (Scores[PatN][WordN][Match].Score > Scores[PatN][WordN][Miss].Score)
337 for (
int W = WordN - 1, P = PatN - 1; W >= 0; --W) {
339 const auto &Cell = Scores[P + 1][W + 1][Last];
342 const auto &Prev = Scores[P + 1][W][Cell.Prev];
343 S[W] = Cell.Score - Prev.Score;
346 for (
int I = 0; I < WordN; ++I) {
347 if (A[I] == Match && (I == 0 || A[I - 1] == Miss))
348 Result.push_back(
'[');
349 if (A[I] == Miss && I > 0 && A[I - 1] == Match)
350 Result.push_back(
']');
351 Result.push_back(Word[I]);
353 if (A[WordN - 1] == Match)
354 Result.push_back(
']');
356 for (
char C : llvm::StringRef(Word, WordN))
357 OS <<
" " << C <<
" ";
359 for (
int I = 0, J = 0; I < WordN; I++)
360 OS <<
" " << (A[I] == Match ? Pat[J++] :
' ') <<
" ";
362 for (
int I = 0; I < WordN; I++)
363 OS << llvm::format(
"%2d ", S[I]);
366 OS <<
"\nSegmentation:";
367 OS <<
"\n'" << llvm::StringRef(Word, WordN) <<
"'\n ";
368 for (
int I = 0; I < WordN; ++I)
369 OS <<
"?-+ "[
static_cast<int>(WordRole[I])];
370 OS <<
"\n[" << llvm::StringRef(Pat, PatN) <<
"]\n ";
371 for (
int I = 0; I < PatN; ++I)
372 OS <<
"?-+ "[
static_cast<int>(PatRole[I])];
375 OS <<
"\nScoring table (last-Miss, last-Match):\n";
377 for (
char C : llvm::StringRef(Word, WordN))
378 OS <<
" " << C <<
" ";
380 OS <<
"-+----" << std::string(WordN * 4,
'-') <<
"\n";
381 for (
int I = 0; I <= PatN; ++I) {
382 for (Action A : {Miss, Match}) {
383 OS << ((I && A == Miss) ? Pat[I - 1] :
' ') <<
"|";
384 for (
int J = 0; J <= WordN; ++J) {
385 if (!
isAwful(Scores[I][J][A].Score))
386 OS << llvm::format(
"%3d%c", Scores[I][J][A].Score,
387 Scores[I][J][A].Prev == Match ?
'*' :
' ');
llvm::SmallString< 256 > dumpLast(llvm::raw_ostream &) const
FuzzyMatcher(llvm::StringRef Pattern)
std::optional< float > match(llvm::StringRef Word)
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
static constexpr int PerfectBonus
unsigned char CharTypeSet
static bool isAwful(int S)
static char lower(char C)
static constexpr uint8_t CharRoles[]
static constexpr uint8_t CharTypes[]
@ Type
An inlay hint that for a type annotation.
static T packedLookup(const uint8_t *Data, int I)
CharTypeSet calculateRoles(llvm::StringRef Text, llvm::MutableArrayRef< CharRole > Roles)
static constexpr int AwfulScore
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//