Skip to content

Commit ca34182

Browse files
committed
feat: colorized search output
1 parent 7ad8804 commit ca34182

File tree

3 files changed

+43
-30
lines changed

3 files changed

+43
-30
lines changed

src/Action/Search.hs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import qualified Data.Set as Set
2121
import System.Directory
2222
import Text.Blaze.Renderer.Utf8
2323
import Safe
24-
import System.Console.ANSI (hSupportsANSI, hyperlinkCode)
24+
import System.Console.ANSI
25+
(hSupportsANSI, hyperlinkCode, setSGRCode
26+
,SGR (SetColor), ConsoleLayer (Foreground)
27+
,ColorIntensity (Vivid, Dull), Color (Yellow))
2528
import System.IO (stdout)
2629

2730
import Action.CmdLine
@@ -46,19 +49,18 @@ actionSearch Search{..} = replicateM_ repeat_ $ -- deliberately reopen the datab
4649
count' <- pure $ fromMaybe 10 count
4750
(q, res) <- pure $ search store $ parseQuery $ unwords query
4851
whenLoud $ putStrLn $ "Query: " ++ unescapeHTML (LBS.unpack $ renderMarkup $ renderQuery q)
49-
hyperlink <- case color of
52+
color' <- case color of
5053
Just b -> pure b
5154
Nothing -> hSupportsANSI stdout
52-
let (shown, hidden) = splitAt count' $ nubOrd $ map (targetResultDisplay link hyperlink) res
55+
let (shown, hidden) = splitAt count' $ nubOrd $ map (targetResultDisplay link color' q) res
5356
if null res then
5457
putStrLn "No results found"
5558
else if info then do
56-
putStr $ targetInfo $ headErr res
59+
putStr $ targetInfo color' q $ headErr res
5760
else do
58-
let toShow = if numbers && not info then addCounter shown else shown
5961
if | json -> LBS.putStrLn $ JSON.encode $ maybe id take count $ map unHTMLtargetItem res
6062
| jsonl -> mapM_ (LBS.putStrLn . JSON.encode) $ maybe id take count $ map unHTMLtargetItem res
61-
| otherwise -> putStr $ unlines toShow
63+
| otherwise -> putStr $ unlines $ if numbers then addCounter shown else shown
6264
when (hidden /= [] && not json) $ do
6365
whenNormal $ putStrLn $ "-- plus more results not shown, pass --count=" ++ show (count'+10) ++ " to see more"
6466
else do
@@ -68,22 +70,29 @@ actionSearch Search{..} = replicateM_ repeat_ $ -- deliberately reopen the datab
6870
putStr $ unlines $ searchFingerprintsDebug store (parseType $ unwords query) (map parseType compare_)
6971

7072
-- | Returns the details printed out when hoogle --info is called
71-
targetInfo :: Target -> String
72-
targetInfo Target{..} =
73-
unlines $ [ unHTML targetItem ] ++
73+
targetInfo :: Bool -> [Query] -> Target -> String
74+
targetInfo color qs Target{..} =
75+
unlines $ [ unHTML . (if color then ansiHighlight qs else id) $ targetItem ] ++
7476
[ unwords packageModule | not $ null packageModule] ++
7577
[ unHTML targetDocs ]
7678
where packageModule = map fst $ catMaybes [targetPackage, targetModule]
7779

7880
-- | Returns the Target formatted as an item to display in the results
79-
-- | Bool argument decides whether links are shown
80-
targetResultDisplay :: Bool -> Bool -> Target -> String
81-
targetResultDisplay link hyperlink Target{..} = unHTML $ unwords $
81+
-- | Bool arguments decide whether links and colors are shown
82+
targetResultDisplay :: Bool -> Bool -> [Query] -> Target -> String
83+
targetResultDisplay link color qs Target{..} = unHTML $ unwords $
8284
map fst (maybeToList targetModule) ++
83-
[if hyperlink then targetItemHyperlink else targetItem] ++
85+
[if color then highlightFull targetItem else targetItem] ++
8486
["-- " ++ targetURL | link]
8587
where
86-
targetItemHyperlink = hyperlinkCode targetURL targetItem
88+
highlightFull = hyperlinkCode targetURL . ansiHighlight qs
89+
90+
ansiHighlight :: [Query] -> String -> String
91+
ansiHighlight = highlightItem id id ((dull ++) . (++ rst)) ((bold ++) . (++ rst))
92+
where
93+
dull = setSGRCode [SetColor Foreground Dull Yellow]
94+
bold = setSGRCode [SetColor Foreground Vivid Yellow]
95+
rst = setSGRCode []
8796

8897
unHTMLtargetItem :: Target -> Target
8998
unHTMLtargetItem target = target {targetItem = unHTML $ targetItem target}

src/Action/Server.hs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -255,23 +255,9 @@ showURL _ _ x = x
255255
-------------------------------------------------------------
256256
-- DISPLAY AN ITEM (bold keywords etc)
257257

258-
highlightItem :: [Query] -> String -> Markup
259-
highlightItem qs x
260-
| Just (pre,x) <- stripInfix "<s0>" x, Just (name,post) <- stripInfix "</s0>" x
261-
= H.preEscapedString pre <> highlight (unescapeHTML name) <> H.preEscapedString post
262-
| otherwise = H.string x
263-
where
264-
highlight = mconcatMap (\xs@((b,_):_) -> let s = H.string $ map snd xs in if b then H.b s else s) .
265-
groupOn fst . (\x -> zip (f x) x)
266-
where
267-
f (x:xs) | m > 0 = replicate m True ++ drop (m - 1) (f xs)
268-
where m = maximum $ 0 : [length y | QueryName y <- qs, lower y `isPrefixOf` lower (x:xs)]
269-
f (x:xs) = False : f xs
270-
f [] = []
271258

272259
displayItem :: [Query] -> String -> Markup
273-
displayItem = highlightItem
274-
260+
displayItem = highlightItem H.string H.preEscapedString H.string (H.b . H.string)
275261

276262
action_server_test_ :: IO ()
277263
action_server_test_ = do

src/Input/Item.hs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
-- | Types used to generate the input.
55
module Input.Item(
66
Sig(..), Ctx(..), Ty(..), prettySig,
7-
Item(..), itemName,
7+
Item(..), itemName, highlightItem,
88
Target(..), targetExpandURL, TargetId(..),
99
splitIPackage, splitIModule,
1010
hseToSig, hseToItem, item_test,
@@ -31,6 +31,7 @@ import qualified Data.Aeson as J
3131
import Data.Aeson.Types
3232
import Test.QuickCheck
3333
import Distribution.Types.PackageName (unPackageName, mkPackageName)
34+
import Query
3435

3536
---------------------------------------------------------------------
3637
-- TYPES
@@ -197,6 +198,23 @@ item_test = testing "Input.Item.Target JSON (encode . decode = id) " $ do
197198
(Left e ) -> False
198199
(Right t') -> t == t'
199200

201+
highlightItem:: Monoid m => (String -> m) -> (String -> m) -> (String -> m) -> (String -> m) -> [Query] -> String -> m
202+
highlightItem plain safe dull bold qs x
203+
| Just (pre,x) <- stripInfix "<s0>" x, Just (name,post) <- stripInfix "</s0>" x
204+
= safe pre <> highlight (unescapeHTML name) <> safe post
205+
| otherwise = plain x
206+
where
207+
highlight x = mconcatMap (\xs@((b,_):_) -> let s = map snd xs in if b then bold s else dull s) $
208+
groupOn fst $ zip (findQueries x) x
209+
where
210+
-- generates a bool mask, which is only true for charachters that compose given queries
211+
-- e.g. [ "query" "ya" ] -> [ "AqUeRyAA" ] -> 01111110
212+
findQueries :: String -> [Bool]
213+
findQueries (x:xs) | m > 0 = replicate m True ++ drop (m - 1) (findQueries xs)
214+
where m = maximum $ 0 : [length y | QueryName y <- qs, lower y `isPrefixOf` lower (x:xs)]
215+
findQueries (x:xs) = False : findQueries xs
216+
findQueries [] = []
217+
200218
---------------------------------------------------------------------
201219
-- HSE CONVERSION
202220

0 commit comments

Comments
 (0)