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,
150 return static_cast<T
>((Data[I >> 2] >> ((I & 3) * 2)) & 3);
153 llvm::MutableArrayRef<CharRole> Roles) {
154 assert(
Text.size() == Roles.size());
155 if (
Text.size() == 0)
163 auto Rotate = [&](
CharType T) { Types = ((Types << 2) | T) & 0x3f; };
164 for (
unsigned I = 0; I <
Text.size() - 1; ++I) {
167 TypeSet |= 1 <<
Type;
179bool FuzzyMatcher::init(llvm::StringRef NewWord) {
180 WordN = std::min<int>(MaxWord, NewWord.size());
183 std::copy(NewWord.begin(), NewWord.begin() + WordN, Word);
186 for (
int I = 0; I < WordN; ++I)
187 LowWord[I] =
lower(Word[I]);
190 for (
int W = 0, P = 0; P != PatN; ++W) {
193 if (LowWord[W] == LowPat[P])
201 llvm::MutableArrayRef(WordRole, WordN));
214void FuzzyMatcher::buildGraph() {
215 for (
int W = 0; W < WordN; ++W) {
216 Scores[0][W + 1][Miss] = {Scores[0][W][Miss].Score - skipPenalty(W, Miss),
220 for (
int P = 0;
P < PatN; ++
P) {
221 for (
int W = P; W < WordN; ++W) {
222 auto &Score = Scores[
P + 1][W + 1], &PreMiss = Scores[
P + 1][W];
224 auto MatchMissScore = PreMiss[Match].Score;
225 auto MissMissScore = PreMiss[Miss].Score;
227 MatchMissScore -= skipPenalty(W, Match);
228 MissMissScore -= skipPenalty(W, Miss);
230 Score[Miss] = (MatchMissScore > MissMissScore)
231 ? ScoreInfo{MatchMissScore, Match}
232 : ScoreInfo{MissMissScore, Miss};
234 auto &PreMatch = Scores[
P][W];
235 auto MatchMatchScore =
236 allowMatch(P, W, Match)
237 ? PreMatch[Match].Score + matchBonus(P, W, Match)
239 auto MissMatchScore = allowMatch(P, W, Miss)
240 ? PreMatch[Miss].Score + matchBonus(P, W, Miss)
242 Score[Match] = (MatchMatchScore > MissMatchScore)
243 ? ScoreInfo{MatchMatchScore, Match}
244 : ScoreInfo{MissMatchScore, Miss};
249bool FuzzyMatcher::allowMatch(
int P,
int W, Action Last)
const {
250 if (LowPat[P] != LowWord[W])
260 if (WordRole[W] ==
Tail &&
261 (Word[W] == LowWord[W] || !(WordTypeSet & 1 <<
Lower)))
267int FuzzyMatcher::skipPenalty(
int W, Action Last)
const {
270 if (WordRole[W] ==
Head)
278int FuzzyMatcher::matchBonus(
int P,
int W, Action Last)
const {
279 assert(LowPat[P] == LowWord[W]);
281 bool IsPatSingleCase =
282 (PatTypeSet == 1 <<
Lower) || (PatTypeSet == 1 <<
Upper);
286 if (Pat[P] == Word[W] ||
287 (WordRole[W] ==
Head && (IsPatSingleCase || PatRole[P] ==
Head)))
291 if (W == 0 || Last == Match)
294 if (WordRole[W] ==
Tail && P && Last == Miss)
297 if (PatRole[P] ==
Head && WordRole[W] ==
Tail)
300 if (P == 0 && WordRole[W] ==
Tail)
307 llvm::SmallString<256> Result;
308 OS <<
"=== Match \"" << llvm::StringRef(Word, WordN) <<
"\" against ["
309 << llvm::StringRef(Pat, PatN) <<
"] ===\n";
311 OS <<
"Pattern is empty: perfect match.\n";
312 return Result = llvm::StringRef(Word, WordN);
315 OS <<
"Word is empty: no match.\n";
318 if (!WordContainsPattern) {
319 OS <<
"Substring check failed.\n";
322 if (
isAwful(std::max(Scores[PatN][WordN][Match].Score,
323 Scores[PatN][WordN][Miss].Score))) {
324 OS <<
"Substring check passed, but all matches are forbidden\n";
326 if (!(PatTypeSet & 1 <<
Upper))
327 OS <<
"Lowercase query, so scoring ignores case\n";
333 (Scores[PatN][WordN][Match].Score > Scores[PatN][WordN][Miss].Score)
338 for (
int W = WordN - 1, P = PatN - 1; W >= 0; --W) {
340 const auto &Cell = Scores[P + 1][W + 1][Last];
343 const auto &Prev = Scores[P + 1][W][Cell.Prev];
344 S[W] = Cell.Score - Prev.Score;
347 for (
int I = 0; I < WordN; ++I) {
348 if (A[I] == Match && (I == 0 || A[I - 1] == Miss))
349 Result.push_back(
'[');
350 if (A[I] == Miss && I > 0 && A[I - 1] == Match)
351 Result.push_back(
']');
352 Result.push_back(Word[I]);
354 if (A[WordN - 1] == Match)
355 Result.push_back(
']');
357 for (
char C : llvm::StringRef(Word, WordN))
358 OS <<
" " << C <<
" ";
360 for (
int I = 0, J = 0; I < WordN; I++)
361 OS <<
" " << (A[I] == Match ? Pat[J++] :
' ') <<
" ";
363 for (
int I = 0; I < WordN; I++)
364 OS << llvm::format(
"%2d ", S[I]);
367 OS <<
"\nSegmentation:";
368 OS <<
"\n'" << llvm::StringRef(Word, WordN) <<
"'\n ";
369 for (
int I = 0; I < WordN; ++I)
370 OS <<
"?-+ "[
static_cast<int>(WordRole[I])];
371 OS <<
"\n[" << llvm::StringRef(Pat, PatN) <<
"]\n ";
372 for (
int I = 0; I < PatN; ++I)
373 OS <<
"?-+ "[
static_cast<int>(PatRole[I])];
376 OS <<
"\nScoring table (last-Miss, last-Match):\n";
378 for (
char C : llvm::StringRef(Word, WordN))
379 OS <<
" " << C <<
" ";
381 OS <<
"-+----" << std::string(WordN * 4,
'-') <<
"\n";
382 for (
int I = 0; I <= PatN; ++I) {
383 for (Action A : {Miss, Match}) {
384 OS << ((I && A == Miss) ? Pat[I - 1] :
' ') <<
"|";
385 for (
int J = 0; J <= WordN; ++J) {
386 if (!
isAwful(Scores[I][J][A].Score))
387 OS << llvm::format(
"%3d%c", Scores[I][J][A].Score,
388 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 T packedLookup(const uint8_t *Data, unsigned char I)
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.
CharTypeSet calculateRoles(llvm::StringRef Text, llvm::MutableArrayRef< CharRole > Roles)
static constexpr int AwfulScore
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//