Skip to content

Commit 2dc615a

Browse files
committed
Inline Text.Fuzzy to add INLINABLE pragmas
1 parent 6f9c448 commit 2dc615a

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

ghcide/ghcide.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ library
5050
dlist,
5151
-- we can't use >= 1.7.10 while we have to use hlint == 3.2.*
5252
extra >= 1.7.4 && < 1.7.10,
53-
fuzzy,
5453
filepath,
5554
fingertree,
5655
ghc-exactprint,
@@ -64,6 +63,7 @@ library
6463
hiedb == 0.4.1.*,
6564
lsp-types >= 1.3.0.1 && < 1.4,
6665
lsp == 1.2.*,
66+
monoid-subclasses,
6767
mtl,
6868
network-uri,
6969
optparse-applicative,
@@ -208,6 +208,8 @@ library
208208
Development.IDE.Plugin.Completions.Logic
209209
Development.IDE.Session.VersionCheck
210210
Development.IDE.Types.Action
211+
Text.Fuzzy
212+
211213
ghc-options: -Wall -Wno-name-shadowing -Wincomplete-uni-patterns -Wno-unticked-promoted-constructors
212214

213215
if flag(ghc-patched-unboxed-bytecode)

ghcide/src/Text/Fuzzy.hs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
{-# LANGUAGE FlexibleContexts #-}
2+
3+
-- | Fuzzy string search in Haskell.
4+
-- Uses 'TextualMonoid' to be able to run on different types of strings.
5+
module Text.Fuzzy where
6+
7+
import Prelude hiding (filter)
8+
import qualified Prelude as P
9+
10+
import Data.Char (toLower)
11+
import Data.List (sortOn)
12+
import Data.Maybe (isJust, mapMaybe)
13+
import Data.Monoid (mempty, (<>))
14+
import Data.Ord
15+
import Data.String
16+
import Data.Text (Text)
17+
18+
import qualified Data.Monoid.Textual as T
19+
20+
-- | Included in the return type of @'match'@ and @'filter'@.
21+
-- Contains the original value given, the rendered string
22+
-- and the matching score.
23+
data (T.TextualMonoid s) => Fuzzy t s =
24+
Fuzzy { original :: t
25+
, rendered :: s
26+
, score :: Int
27+
} deriving (Show, Eq)
28+
29+
-- | Returns the rendered output and the
30+
-- matching score for a pattern and a text.
31+
-- Two examples are given below:
32+
--
33+
-- >>> match "fnt" "infinite" "" "" id True
34+
-- Just ("infinite",3)
35+
--
36+
-- >>> match "hsk" ("Haskell",1995) "<" ">" fst False
37+
-- Just ("<h>a<s><k>ell",5)
38+
--
39+
match :: (T.TextualMonoid s)
40+
=> s -- ^ Pattern.
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+
-> Bool -- ^ Case sensitivity.
46+
-> Maybe (Fuzzy t s) -- ^ The original value, rendered string and score.
47+
match pattern t pre post extract caseSensitive =
48+
if null pat then Just (Fuzzy t result totalScore) else Nothing
49+
where
50+
null :: (T.TextualMonoid s) => s -> Bool
51+
null = not . T.any (const True)
52+
53+
s = extract t
54+
(s', pattern') = let f = T.map toLower in
55+
if caseSensitive then (s, pattern) else (f s, f pattern)
56+
57+
(totalScore, currScore, result, pat) =
58+
T.foldl'
59+
undefined
60+
(\(tot, cur, res, pat) c ->
61+
case T.splitCharacterPrefix pat of
62+
Nothing -> (tot, 0, res <> T.singleton c, pat)
63+
Just (x, xs) ->
64+
if x == c then
65+
let cur' = cur * 2 + 1 in
66+
(tot + cur', cur', res <> pre <> T.singleton c <> post, xs)
67+
else (tot, 0, res <> T.singleton c, pat)
68+
) (0, 0, mempty, pattern') s'
69+
70+
-- | The function to filter a list of values by fuzzy search on the text extracted from them.
71+
--
72+
-- >>> filter "ML" [("Standard ML", 1990),("OCaml",1996),("Scala",2003)] "<" ">" fst False
73+
-- [Fuzzy {original = ("Standard ML",1990), rendered = "standard <m><l>", score = 4},Fuzzy {original = ("OCaml",1996), rendered = "oca<m><l>", score = 4}]
74+
filter :: (T.TextualMonoid s)
75+
=> s -- ^ Pattern.
76+
-> [t] -- ^ The list of values containing the text to search in.
77+
-> s -- ^ The text to add before each match.
78+
-> s -- ^ The text to add after each match.
79+
-> (t -> s) -- ^ The function to extract the text from the container.
80+
-> Bool -- ^ Case sensitivity.
81+
-> [Fuzzy t s] -- ^ The list of results, sorted, highest score first.
82+
filter pattern ts pre post extract caseSen =
83+
sortOn (Down . score)
84+
(mapMaybe (\t -> match pattern t pre post extract caseSen) ts)
85+
86+
filterText :: Text -> [Text] -> [Fuzzy Text Text]
87+
filterText s t = filter s t "" "" id False
88+
89+
{-# SPECIALIZE simpleFilter :: Text -> [Text] -> [Fuzzy Text Text] #-}
90+
91+
-- | Return all elements of the list that have a fuzzy
92+
-- match against the pattern. Runs with default settings where
93+
-- nothing is added around the matches, as case insensitive.
94+
--
95+
-- >>> simpleFilter "vm" ["vim", "emacs", "virtual machine"]
96+
-- ["vim","virtual machine"]
97+
simpleFilter :: (T.TextualMonoid s)
98+
=> s -- ^ Pattern to look for.
99+
-> [s] -- ^ List of texts to check.
100+
-> [Fuzzy s s] -- ^ The ones that match.
101+
simpleFilter pattern xs =
102+
filter pattern xs mempty mempty id False
103+
104+
-- | Returns false if the pattern and the text do not match at all.
105+
-- Returns true otherwise.
106+
--
107+
-- >>> test "brd" "bread"
108+
-- True
109+
test :: (T.TextualMonoid s)
110+
=> s -> s -> Bool
111+
test p s = isJust (match p s mempty mempty id False)
112+
113+
114+
{-# INLINABLE match #-}
115+
{-# INLINABLE filter #-}
116+
{-# INLINABLE simpleFilter #-}

0 commit comments

Comments
 (0)