@@ -5,7 +5,6 @@ module Text.Fuzzy.Parallel
5
5
Scored (.. ),
6
6
-- reexports
7
7
Fuzzy ,
8
- match
9
8
) where
10
9
11
10
import Control.Monad.ST (runST )
@@ -16,12 +15,58 @@ import Data.Vector (Vector, (!))
16
15
import qualified Data.Vector as V
17
16
-- need to use a stable sort
18
17
import Data.Bifunctor (second )
19
- import Data.Maybe (fromJust )
18
+ import Data.Char (toLower )
19
+ import Data.Maybe (fromMaybe )
20
+ import qualified Data.Monoid.Textual as T
20
21
import Prelude hiding (filter )
21
- import Text.Fuzzy (Fuzzy (.. ), match )
22
+ import Text.Fuzzy (Fuzzy (.. ))
22
23
23
24
data Scored a = Scored { score_ :: ! Int , original :: ! a }
24
- deriving Functor
25
+ deriving (Functor ,Show )
26
+
27
+ -- | Returns the rendered output and the
28
+ -- matching score for a pattern and a text.
29
+ -- Two examples are given below:
30
+ --
31
+ -- >>> match "fnt" "infinite" "" "" id True
32
+ -- Just ("infinite",3)
33
+ --
34
+ -- >>> match "hsk" ("Haskell",1995) "<" ">" fst False
35
+ -- Just ("<h>a<s><k>ell",5)
36
+ --
37
+ {-# INLINABLE match #-}
38
+
39
+ match :: (T. TextualMonoid s )
40
+ => s -- ^ Pattern in lowercase except for first character
41
+ -> t -- ^ The value containing the text to search in.
42
+ -> s -- ^ The text to add before each match.
43
+ -> s -- ^ The text to add after each match.
44
+ -> (t -> s ) -- ^ The function to extract the text from the container.
45
+ -> Maybe (Fuzzy t s ) -- ^ The original value, rendered string and score.
46
+ match pattern t pre post extract =
47
+ if null pat then Just (Fuzzy t result totalScore) else Nothing
48
+ where
49
+ null :: (T. TextualMonoid s ) => s -> Bool
50
+ null = not . T. any (const True )
51
+
52
+ s = extract t
53
+ (totalScore, _currScore, result, pat, _) =
54
+ T. foldl'
55
+ undefined
56
+ (\ (tot, cur, res, pat, isFirst) c ->
57
+ case T. splitCharacterPrefix pat of
58
+ Nothing -> (tot, 0 , res <> T. singleton c, pat, isFirst)
59
+ Just (x, xs) ->
60
+ -- the case of the first character has to match
61
+ -- otherwise use lower case since the pattern is assumed lower
62
+ let ! c' = if isFirst then c else toLower c in
63
+ if x == c' then
64
+ let cur' = cur * 2 + 1 in
65
+ (tot + cur', cur', res <> pre <> T. singleton c <> post, xs, False )
66
+ else (tot, 0 , res <> T. singleton c, pat, isFirst)
67
+ ) ( 0
68
+ , 1 -- matching at the start gives a bonus (cur = 1)
69
+ , mempty , pattern , True ) s
25
70
26
71
-- | The function to filter a list of values by fuzzy search on the text extracted from them.
27
72
filter :: (TextualMonoid s )
@@ -32,15 +77,20 @@ filter :: (TextualMonoid s)
32
77
-> s -- ^ The text to add before each match.
33
78
-> s -- ^ The text to add after each match.
34
79
-> (t -> s ) -- ^ The function to extract the text from the container.
35
- -> Bool -- ^ Case sensitivity.
36
80
-> [Scored t ] -- ^ The list of results, sorted, highest score first.
37
- filter chunkSize maxRes pattern ts pre post extract caseSen = runST $ do
81
+ filter chunkSize maxRes pattern ts pre post extract = runST $ do
38
82
let v = V. mapMaybe id
39
- (V. map (\ t -> match pattern t pre post extract caseSen ) (V. fromList ts)
83
+ (V. map (\ t -> match pattern' t pre post extract) (V. fromList ts)
40
84
`using`
41
85
parVectorChunk chunkSize (evalTraversable forceScore))
42
- perfectScore = score $ fromJust $ match pattern pattern " " " " id False
86
+ perfectScore = score $ fromMaybe (error $ T. toString undefined pattern ) $
87
+ match pattern' pattern' " " " " id
43
88
return $ partialSortByAscScore maxRes perfectScore v
89
+ where
90
+ -- Preserve case for the first character, make all others lowercase
91
+ pattern' = case T. splitCharacterPrefix pattern of
92
+ Just (c, rest) -> T. singleton c <> T. map toLower rest
93
+ _ -> pattern
44
94
45
95
-- | Return all elements of the list that have a fuzzy
46
96
-- match against the pattern. Runs with default settings where
@@ -56,7 +106,7 @@ simpleFilter :: (TextualMonoid s)
56
106
-> [s ] -- ^ List of texts to check.
57
107
-> [Scored s ] -- ^ The ones that match.
58
108
simpleFilter chunk maxRes pattern xs =
59
- filter chunk maxRes pattern xs mempty mempty id False
109
+ filter chunk maxRes pattern xs mempty mempty id
60
110
61
111
--------------------------------------------------------------------------------
62
112
0 commit comments