Skip to content

Commit 72684d4

Browse files
committed
Efficiently with vectors
1 parent 979bacf commit 72684d4

File tree

4 files changed

+68
-27
lines changed

4 files changed

+68
-27
lines changed

ghcide/ghcide.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ library
8787
unordered-containers >= 0.2.10.0,
8888
utf8-string,
8989
vector,
90+
vector-algorithms,
9091
hslogger,
9192
Diff ^>=0.4.0,
9293
vector,

ghcide/src/Development/IDE/Plugin/Completions/Logic.hs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -494,14 +494,14 @@ ppr :: Outputable a => a -> T.Text
494494
ppr = T.pack . prettyPrint
495495

496496
toggleSnippets :: ClientCapabilities -> CompletionsConfig -> CompletionItem -> CompletionItem
497-
toggleSnippets ClientCapabilities {_textDocument} (CompletionsConfig with _) =
498-
removeSnippetsWhen (not $ with && supported)
497+
toggleSnippets ClientCapabilities {_textDocument} CompletionsConfig{..} =
498+
removeSnippetsWhen (not $ enableSnippets && supported)
499499
where
500500
supported =
501501
Just True == (_textDocument >>= _completion >>= _completionItem >>= _snippetSupport)
502502

503503
toggleAutoExtend :: CompletionsConfig -> CompItem -> CompItem
504-
toggleAutoExtend (CompletionsConfig _ False) x = x {additionalTextEdits = Nothing}
504+
toggleAutoExtend CompletionsConfig{enableAutoExtend=False} x = x {additionalTextEdits = Nothing}
505505
toggleAutoExtend _ x = x
506506

507507
removeSnippetsWhen :: Bool -> CompletionItem -> CompletionItem
@@ -539,12 +539,14 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu
539539
-}
540540
pos = VFS.cursorPos prefixInfo
541541

542+
maxC = maxCompletions config
543+
542544
filtModNameCompls =
543545
map mkModCompl
544546
$ mapMaybe (T.stripPrefix enteredQual)
545-
$ Fuzzy.simpleFilter chunkSize fullPrefix allModNamesAsNS
547+
$ Fuzzy.simpleFilter chunkSize maxC fullPrefix allModNamesAsNS
546548

547-
filtCompls = map Fuzzy.original $ Fuzzy.filter chunkSize prefixText ctxCompls "" "" label False
549+
filtCompls = map Fuzzy.original $ Fuzzy.filter chunkSize maxC prefixText ctxCompls "" "" label False
548550
where
549551

550552
mcc = case maybe_parsed of
@@ -591,7 +593,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu
591593

592594
filtListWith f list =
593595
[ f label
594-
| label <- Fuzzy.simpleFilter chunkSize fullPrefix list
596+
| label <- Fuzzy.simpleFilter chunkSize maxC fullPrefix list
595597
, enteredQual `T.isPrefixOf` label
596598
]
597599

ghcide/src/Development/IDE/Plugin/Completions/Types.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import qualified Data.Text as T
1212

1313
import Data.Aeson (FromJSON, ToJSON)
1414
import Data.Text (Text)
15-
import Development.IDE.Spans.Common
1615
import Development.IDE.GHC.Compat
16+
import Development.IDE.Spans.Common
1717
import GHC.Generics (Generic)
1818
import Ide.Plugin.Config (Config)
19+
import qualified Ide.Plugin.Config as Config
1920
import Ide.Plugin.Properties
20-
import Ide.PluginUtils (usePropertyLsp)
21+
import Ide.PluginUtils (getClientConfig, usePropertyLsp)
2122
import Ide.Types (PluginId)
2223
import Language.LSP.Server (MonadLsp)
2324
import Language.LSP.Types (CompletionItemKind (..), Uri)
@@ -46,11 +47,13 @@ getCompletionsConfig pId =
4647
CompletionsConfig
4748
<$> usePropertyLsp #snippetsOn pId properties
4849
<*> usePropertyLsp #autoExtendOn pId properties
50+
<*> (Config.maxCompletions <$> getClientConfig)
4951

5052

5153
data CompletionsConfig = CompletionsConfig {
5254
enableSnippets :: Bool,
53-
enableAutoExtend :: Bool
55+
enableAutoExtend :: Bool,
56+
maxCompletions :: Int
5457
}
5558

5659
data ExtendImport = ExtendImport

ghcide/src/Text/Fuzzy/Parallel.hs

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,44 @@ module Text.Fuzzy.Parallel
88
match
99
) where
1010

11-
import Control.Parallel.Strategies (Eval, evalTraversable,
12-
parListChunk, rseq, using)
11+
import Control.Monad.ST (runST)
12+
import Control.Parallel.Strategies (Eval, Strategy, evalTraversable,
13+
parListChunk, parTraversable,
14+
rseq, using)
15+
import Data.Function (on)
1316
import Data.List (sortOn)
1417
import Data.Maybe (catMaybes)
1518
import Data.Monoid.Textual (TextualMonoid)
1619
import Data.Ord (Down (Down))
20+
import Data.Vector (Vector, (!))
21+
import qualified Data.Vector as V
22+
import qualified Data.Vector.Algorithms.Heap as VA
1723
import Prelude hiding (filter)
1824
import Text.Fuzzy (Fuzzy (..), match)
1925

20-
-- | Evaluation that forces the 'score' field
21-
forceScore :: TextualMonoid s => Fuzzy t s -> Eval(Fuzzy t s)
22-
forceScore it@Fuzzy{score} = do
23-
score' <- rseq score
24-
return it{score = score'}
25-
2626
-- | The function to filter a list of values by fuzzy search on the text extracted from them.
2727
--
28-
-- >>> filter "ML" [("Standard ML", 1990),("OCaml",1996),("Scala",2003)] "<" ">" fst False
29-
-- [Fuzzy {original = ("Standard ML",1990), rendered = "standard <m><l>", score = 4},Fuzzy {original = ("OCaml",1996), rendered = "oca<m><l>", score = 4}]
30-
{-# INLINABLE filter #-}
28+
-- >>> length $ filter 1000 200 "ML" (concat $ replicate 10000 [("Standard ML", 1990),("OCaml",1996),("Scala",2003)]) "<" ">" fst False
29+
-- 200
3130
filter :: (TextualMonoid s)
3231
=> Int -- ^ Chunk size. 1000 works well.
32+
-> Int -- ^ Max results
3333
-> s -- ^ Pattern.
3434
-> [t] -- ^ The list of values containing the text to search in.
3535
-> s -- ^ The text to add before each match.
3636
-> s -- ^ The text to add after each match.
3737
-> (t -> s) -- ^ The function to extract the text from the container.
3838
-> Bool -- ^ Case sensitivity.
3939
-> [Fuzzy t s] -- ^ The list of results, sorted, highest score first.
40-
filter chunkSize pattern ts pre post extract caseSen =
41-
sortOn (Down . score)
42-
(catMaybes
43-
(map (\t -> match pattern t pre post extract caseSen) ts
40+
filter chunkSize maxRes pattern ts pre post extract caseSen = runST $ do
41+
let v = (V.catMaybes
42+
(V.map (\t -> match pattern t pre post extract caseSen) (V.fromList ts)
4443
`using`
45-
parListChunk chunkSize (evalTraversable forceScore)))
44+
parVectorChunk chunkSize (evalTraversable forceScore)))
45+
v' <- V.unsafeThaw v
46+
VA.partialSortBy (compare `on` (Down . score)) v' maxRes
47+
v'' <- V.unsafeFreeze v'
48+
return $ take maxRes $ V.toList v''
4649

4750
-- | Return all elements of the list that have a fuzzy
4851
-- match against the pattern. Runs with default settings where
@@ -53,8 +56,40 @@ filter chunkSize pattern ts pre post extract caseSen =
5356
{-# INLINABLE simpleFilter #-}
5457
simpleFilter :: (TextualMonoid s)
5558
=> Int -- ^ Chunk size. 1000 works well.
59+
-> Int -- ^ Max results
5660
-> s -- ^ Pattern to look for.
5761
-> [s] -- ^ List of texts to check.
5862
-> [s] -- ^ The ones that match.
59-
simpleFilter chunk pattern xs =
60-
map original $ filter chunk pattern xs mempty mempty id False
63+
simpleFilter chunk maxRes pattern xs =
64+
map original $ filter chunk maxRes pattern xs mempty mempty id False
65+
66+
--------------------------------------------------------------------------------
67+
68+
-- | Divides a vector in chunks, applies the strategy in parallel to each chunk.
69+
parVectorChunk :: Int -> Strategy a -> Vector a -> Eval (Vector a)
70+
parVectorChunk chunkSize st v =
71+
V.concat <$> parTraversable (evalTraversable st) (chunkVector chunkSize v)
72+
73+
-- >>> chunkVector 3 (V.fromList [0..10])
74+
-- >>> chunkVector 3 (V.fromList [0..11])
75+
-- >>> chunkVector 3 (V.fromList [0..12])
76+
-- [[0,1,2],[3,4,5],[6,7,8],[9,10]]
77+
-- [[0,1,2],[3,4,5],[6,7,8],[9,10,11]]
78+
-- [[0,1,2],[3,4,5],[6,7,8],[9,10,11],[12]]
79+
chunkVector :: Int -> Vector a -> [Vector a]
80+
chunkVector chunkSize v = do
81+
let indices = pairwise $ [0, chunkSize .. l-1] ++ [l]
82+
l = V.length v
83+
[V.fromListN (h-l) [v ! j | j <- [l .. h-1]]
84+
| (l,h) <- indices]
85+
86+
pairwise :: [a] -> [(a,a)]
87+
pairwise [] = []
88+
pairwise [_] = []
89+
pairwise (x:y:xs) = (x,y) : pairwise (y:xs)
90+
91+
-- | Evaluation that forces the 'score' field
92+
forceScore :: TextualMonoid s => Fuzzy t s -> Eval(Fuzzy t s)
93+
forceScore it@Fuzzy{score} = do
94+
score' <- rseq score
95+
return it{score = score'}

0 commit comments

Comments
 (0)