1{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE ConstraintKinds #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE InstanceSigs #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE RankNTypes #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE UndecidableInstances #-} -- XXX
15-- Module : Streamly.Streams.StreamK
16-- Copyright : (c) 2017 Harendra Kumar
19-- Maintainer : harendra.kumar@gmail.com
20-- Stability : experimental
24-- Continuation passing style (CPS) stream implementation. The symbol 'K' below
25-- denotes a function as well as a Kontinuation.
28-- import qualified Streamly.Streams.StreamK as K
31module Streamly.Streams.StreamK
33 -- * A class for streams
40 -- * Construction Primitives
46 -- * Elimination Primitives
51 -- * Transformation Primitives
62 -- ** Specialized Generation
84 -- ** Specialized Folds
113 -- ** By folding (scans)
147 -- ** Transformation comprehensions
150 -- * Semigroup Style Composition
159 , Streaming -- deprecated
164import Control.Monad (void)
165import Control.Monad.Reader.Class (MonadReader(..))
167 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
168 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
169 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
170 foldr1, (!!), replicate)
171import qualified Prelude
174import Streamly.Streams.StreamK.Type
176-- | Detach a stream from an SVar
177{-# INLINE unShare #-}
178unShare :: IsStream t => t m a -> t m a
179unShare x = mkStream $ \st yld sng stp ->
180 foldStream st yld sng stp x
182------------------------------------------------------------------------------
184------------------------------------------------------------------------------
188-- faster than consM because there is no bind.
189-- | Construct a stream by adding a pure value at the head of an existing
190-- stream. For serial streams this is the same as @(return a) \`consM` r@ but
191-- more efficient. For concurrent streams this is not concurrent whereas
192-- 'consM' is concurrent. For example:
195-- > toList $ 1 \`cons` 2 \`cons` 3 \`cons` nil
200cons :: IsStream t => a -> t m a -> t m a
201cons a r = mkStream $ \_ yld _ _ -> yld a r
205-- | Operator equivalent of 'cons'.
208-- > toList $ 1 .: 2 .: 3 .: nil
213(.:) :: IsStream t => a -> t m a -> t m a
216-------------------------------------------------------------------------------
218-------------------------------------------------------------------------------
221uncons :: (IsStream t, Monad m) => t m a -> m (Maybe (a, t m a))
223 let stop = return Nothing
224 single a = return (Just (a, nil))
225 yieldk a r = return (Just (a, r))
226 in foldStream defState yieldk single stop m
228-------------------------------------------------------------------------------
230-------------------------------------------------------------------------------
232{-# INLINE_NORMAL build #-}
233build :: IsStream t => forall a. (forall b. (a -> b -> b) -> b -> b) -> t m a
236{-# INLINE_NORMAL _augment #-}
239 => forall a. (forall b. (a -> b -> b) -> b -> b) -> t m a -> t m a
240_augment g xs = g cons xs
242{-# INLINE_NORMAL _buildM #-}
244 :: (IsStream t, MonadAsync m)
245 => forall a. ((m a -> t m a -> t m a) -> t m a -> t m a) -> t m a
246_buildM g = g consM nil
248{-# INLINE unfoldr #-}
249unfoldr :: IsStream t => (b -> Maybe (a, b)) -> b -> t m a
250unfoldr step b0 = build $ \cns nl ->
253 Just (a, b) -> a `cns` go b
257{-# INLINE unfoldrM #-}
258unfoldrM :: (IsStream t, MonadAsync m) => (b -> m (Maybe (a, b))) -> b -> t m a
261 go s = mkStream $ \st yld sng stp -> do
266 foldStreamShared st yld sng stp $ return a |: go b
268-------------------------------------------------------------------------------
270-------------------------------------------------------------------------------
272yield :: IsStream t => a -> t m a
273yield a = mkStream $ \_ _ single _ -> single a
278{-# DEPRECATED once "Please use yieldM instead." #-}
280once :: (Monad m, IsStream t) => m a -> t m a
285-- repeatM = fix . cons
286-- repeatM = cycle1 . yield
289-- Generate an infinite stream by repeating a pure value.
292repeat :: IsStream t => a -> t m a
293repeat a = let x = cons a x in x
295replicateM :: (IsStream t, MonadAsync m) => Int -> m a -> t m a
298 go cnt = if cnt <= 0 then nil else m |: go (cnt - 1)
300replicate :: IsStream t => Int -> a -> t m a
303 go cnt = if cnt <= 0 then nil else a `cons` go (cnt - 1)
305-------------------------------------------------------------------------------
307-------------------------------------------------------------------------------
311-- fromFoldable = 'Prelude.foldr' 'cons' 'nil'
314-- Construct a stream from a 'Foldable' containing pure values:
317{-# INLINE fromFoldable #-}
318fromFoldable :: (IsStream t, Foldable f) => f a -> t m a
319fromFoldable = Prelude.foldr cons nil
321{-# INLINE fromList #-}
322fromList :: IsStream t => [a] -> t m a
323fromList = fromFoldable
325{-# INLINE fromStreamK #-}
326fromStreamK :: IsStream t => Stream m a -> t m a
327fromStreamK = fromStream
329-------------------------------------------------------------------------------
330-- Elimination by Folding
331-------------------------------------------------------------------------------
333-- | Lazy right associative fold.
334foldr :: (IsStream t, Monad m) => (a -> b -> b) -> b -> t m a -> m b
335foldr step acc m = go m
338 let stop = return acc
339 single a = return (step a acc)
340 yieldk a r = go r >>= \b -> return (step a b)
341 in foldStream defState yieldk single stop m1
343-- | Lazy right fold with a monadic step function.
345foldrM :: (IsStream t, Monad m) => (a -> b -> m b) -> b -> t m a -> m b
346foldrM step acc m = go m
349 let stop = return acc
350 single a = step a acc
351 yieldk a r = go r >>= step a
352 in foldStream defState yieldk single stop m1
355foldr1 :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> m (Maybe a)
359 Nothing -> return Nothing
360 Just (h, t) -> fmap Just (go h t)
364 single a = return $ step a p
365 yieldk a r = fmap (step p) (go a r)
366 in foldStream defState yieldk single stp m1
368-- | Strict left fold with an extraction function. Like the standard strict
369-- left fold, but applies a user supplied extraction function (the third
370-- argument) to the folded value at the end. This is designed to work with the
371-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
373-- Note that the accumulator is always evaluated including the initial value.
375foldx :: forall t m a b x. (IsStream t, Monad m)
376 => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
377foldx step begin done m = get $ go m begin
382 -- XXX we are not strictly evaluating the accumulator here. Is this
384 let single = return . done
385 -- XXX this is foldSingleton. why foldStreamShared?
386 in foldStreamShared undefined undefined single undefined m1
388 -- Note, this can be implemented by making a recursive call to "go",
389 -- however that is more expensive because of unnecessary recursion
390 -- that cannot be tail call optimized. Unfolding recursion explicitly via
391 -- continuations is much more efficient.
392 go :: t m a -> x -> t m x
393 go m1 !acc = mkStream $ \_ yld sng _ ->
395 single a = sng $ step acc a
396 -- XXX this is foldNonEmptyStream
397 yieldk a r = foldStream defState yld sng undefined $
399 in foldStream defState yieldk single stop m1
401-- | Strict left associative fold.
403foldl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> m b
404foldl' step begin = foldx step begin id
406-- XXX replace the recursive "go" with explicit continuations.
407-- | Like 'foldx', but with a monadic step function.
408foldxM :: (IsStream t, Monad m)
409 => (x -> a -> m x) -> m x -> (x -> m b) -> t m a -> m b
410foldxM step begin done m = go begin m
413 let stop = acc >>= done
414 single a = acc >>= \b -> step b a >>= done
415 yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r
416 in foldStream defState yieldk single stop m1
418-- | Like 'foldl'' but with a monadic step function.
419foldlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> m b
420foldlM' step begin = foldxM step (return begin) return
422------------------------------------------------------------------------------
424------------------------------------------------------------------------------
427-- > runStream = foldl' (\_ _ -> ()) ()
428-- > runStream = mapM_ (\_ -> return ())
429{-# INLINE runStream #-}
430runStream :: (Monad m, IsStream t) => t m a -> m ()
437 in foldStream defState yieldk single stop m1
440null :: (IsStream t, Monad m) => t m a -> m Bool
442 let stop = return True
443 single _ = return False
444 yieldk _ _ = return False
445 in foldStream defState yieldk single stop m
448head :: (IsStream t, Monad m) => t m a -> m (Maybe a)
450 let stop = return Nothing
451 single a = return (Just a)
452 yieldk a _ = return (Just a)
453 in foldStream defState yieldk single stop m
456tail :: (IsStream t, Monad m) => t m a -> m (Maybe (t m a))
458 let stop = return Nothing
459 single _ = return $ Just nil
460 yieldk _ r = return $ Just r
461 in foldStream defState yieldk single stop m
464init :: (IsStream t, Monad m) => t m a -> m (Maybe (t m a))
470 Nothing -> return Nothing
471 Just (h, t) -> return . Just $ go h t
472 go p m1 = mkStream $ \_ yld sng stp ->
474 yieldk a x = yld p $ go a x
475 in foldStream defState yieldk single stp m1
478elem :: (IsStream t, Monad m, Eq a) => a -> t m a -> m Bool
482 let stop = return False
483 single a = return (a == e)
484 yieldk a r = if a == e then return True else go r
485 in foldStream defState yieldk single stop m1
487{-# INLINE notElem #-}
488notElem :: (IsStream t, Monad m, Eq a) => a -> t m a -> m Bool
492 let stop = return True
493 single a = return (a /= e)
494 yieldk a r = if a == e then return False else go r
495 in foldStream defState yieldk single stop m1
497all :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> m Bool
501 let single a | p a = return True
502 | otherwise = return False
503 yieldk a r | p a = go r
504 | otherwise = return False
505 in foldStream defState yieldk single (return True) m1
507any :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> m Bool
511 let single a | p a = return True
512 | otherwise = return False
513 yieldk a r | p a = return True
515 in foldStream defState yieldk single (return False) m1
517-- | Extract the last element of the stream, if any.
519last :: (IsStream t, Monad m) => t m a -> m (Maybe a)
520last = foldx (\_ y -> Just y) Nothing id
522{-# INLINE minimum #-}
523minimum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
524minimum m = go Nothing m
527 let stop = return Nothing
528 single a = return (Just a)
529 yieldk a r = go (Just a) r
530 in foldStream defState yieldk single stop m1
533 let stop = return (Just res)
536 then return (Just res)
542 in foldStream defState yieldk single stop m1
544{-# INLINE minimumBy #-}
546 :: (IsStream t, Monad m)
547 => (a -> a -> Ordering) -> t m a -> m (Maybe a)
548minimumBy cmp m = go Nothing m
551 let stop = return Nothing
552 single a = return (Just a)
553 yieldk a r = go (Just a) r
554 in foldStream defState yieldk single stop m1
557 let stop = return (Just res)
558 single a = case cmp res a of
559 GT -> return (Just a)
560 _ -> return (Just res)
561 yieldk a r = case cmp res a of
564 in foldStream defState yieldk single stop m1
566{-# INLINE maximum #-}
567maximum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
568maximum m = go Nothing m
571 let stop = return Nothing
572 single a = return (Just a)
573 yieldk a r = go (Just a) r
574 in foldStream defState yieldk single stop m1
577 let stop = return (Just res)
581 else return (Just res)
586 in foldStream defState yieldk single stop m1
588{-# INLINE maximumBy #-}
589maximumBy :: (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> m (Maybe a)
590maximumBy cmp m = go Nothing m
593 let stop = return Nothing
594 single a = return (Just a)
595 yieldk a r = go (Just a) r
596 in foldStream defState yieldk single stop m1
599 let stop = return (Just res)
600 single a = case cmp res a of
601 GT -> return (Just res)
603 yieldk a r = case cmp res a of
604 GT -> go (Just res) r
606 in foldStream defState yieldk single stop m1
609(!!) :: (IsStream t, Monad m) => t m a -> Int -> m (Maybe a)
613 let single a | n == 0 = return $ Just a
614 | otherwise = return Nothing
615 yieldk a x | n < 0 = return Nothing
616 | n == 0 = return $ Just a
617 | otherwise = go (n - 1) x
618 in foldStream defState yieldk single (return Nothing) m1
621lookup :: (IsStream t, Monad m, Eq a) => a -> t m (a, b) -> m (Maybe b)
625 let single (a, b) | a == e = return $ Just b
626 | otherwise = return Nothing
627 yieldk (a, b) x | a == e = return $ Just b
629 in foldStream defState yieldk single (return Nothing) m1
632findM :: (IsStream t, Monad m) => (a -> m Bool) -> t m a -> m (Maybe a)
638 if b then return $ Just a else return Nothing
641 if b then return $ Just a else go x
642 in foldStream defState yieldk single (return Nothing) m1
645find :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> m (Maybe a)
646find p = findM (return . p)
648{-# INLINE findIndices #-}
649findIndices :: IsStream t => (a -> Bool) -> t m a -> t m Int
652 go offset m1 = mkStream $ \st yld sng stp ->
653 let single a | p a = sng offset
655 yieldk a x | p a = yld offset $ go (offset + 1) x
656 | otherwise = foldStream (adaptState st) yld sng stp $
658 in foldStream (adaptState st) yieldk single stp m1
660------------------------------------------------------------------------------
662------------------------------------------------------------------------------
664-- | Apply a monadic action to each element of the stream and discard the
665-- output of the action.
667mapM_ :: (IsStream t, Monad m) => (a -> m b) -> t m a -> m ()
672 single a = void (f a)
673 yieldk a r = f a >> go r
674 in foldStream defState yieldk single stop m1
676------------------------------------------------------------------------------
678------------------------------------------------------------------------------
680{-# INLINABLE toList #-}
681toList :: (IsStream t, Monad m) => t m a -> m [a]
684{-# INLINE toStreamK #-}
685toStreamK :: Stream m a -> Stream m a
688-------------------------------------------------------------------------------
689-- Transformation by folding (Scans)
690-------------------------------------------------------------------------------
693scanx :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
694scanx step begin done m =
695 cons (done begin) $ go m begin
697 go m1 !acc = mkStream $ \st yld sng stp ->
698 let single a = sng (done $ step acc a)
701 in yld (done s) (go r s)
702 in foldStream (adaptState st) yieldk single stp m1
705scanl' :: IsStream t => (b -> a -> b) -> b -> t m a -> t m b
706scanl' step begin = scanx step begin id
708-------------------------------------------------------------------------------
710-------------------------------------------------------------------------------
713filter :: IsStream t => (a -> Bool) -> t m a -> t m a
716 go m1 = mkStream $ \st yld sng stp ->
717 let single a | p a = sng a
719 yieldk a r | p a = yld a (go r)
720 | otherwise = foldStream st yieldk single stp r
721 in foldStream st yieldk single stp m1
724take :: IsStream t => Int -> t m a -> t m a
727 go n1 m1 = mkStream $ \st yld sng stp ->
728 let yieldk a r = yld a (go (n1 - 1) r)
731 else foldStream st yieldk sng stp m1
733{-# INLINE takeWhile #-}
734takeWhile :: IsStream t => (a -> Bool) -> t m a -> t m a
737 go m1 = mkStream $ \st yld sng stp ->
738 let single a | p a = sng a
740 yieldk a r | p a = yld a (go r)
742 in foldStream st yieldk single stp m1
744drop :: IsStream t => Int -> t m a -> t m a
747 go n1 m1 = mkStream $ \st yld sng stp ->
749 yieldk _ r = foldStream st yld sng stp $ go (n1 - 1) r
750 -- Somehow "<=" check performs better than a ">"
752 then foldStream st yld sng stp m1
753 else foldStream st yieldk single stp m1
755{-# INLINE dropWhile #-}
756dropWhile :: IsStream t => (a -> Bool) -> t m a -> t m a
759 go m1 = mkStream $ \st yld sng stp ->
760 let single a | p a = stp
762 yieldk a r | p a = foldStream st yieldk single stp r
763 | otherwise = yld a r
764 in foldStream st yieldk single stp m1
766-------------------------------------------------------------------------------
768-------------------------------------------------------------------------------
770-- Be careful when modifying this, this uses a consM (|:) deliberately to allow
771-- other stream types to overload it.
773mapM :: (IsStream t, MonadAsync m) => (a -> m b) -> t m a -> t m b
776 go m1 = mkStream $ \st yld sng stp ->
777 let single a = f a >>= sng
778 yieldk a r = foldStreamShared st yld sng stp $ f a |: go r
779 in foldStream (adaptState st) yieldk single stp m1
781-- Be careful when modifying this, this uses a consM (|:) deliberately to allow
782-- other stream types to overload it.
783{-# INLINE sequence #-}
784sequence :: (IsStream t, MonadAsync m) => t m (m a) -> t m a
787 go m1 = mkStream $ \st yld sng stp ->
788 let single ma = ma >>= sng
789 yieldk ma r = foldStreamShared st yld sng stp $ ma |: go r
790 in foldStream (adaptState st) yieldk single stp m1
792-------------------------------------------------------------------------------
794-------------------------------------------------------------------------------
796{-# INLINE intersperseM #-}
797intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
798intersperseM a m = prependingStart m
800 prependingStart m1 = mkStream $ \st yld sng stp ->
801 let yieldk i x = foldStreamShared st yld sng stp $ return i |: go x
802 in foldStream st yieldk sng stp m1
803 go m2 = mkStream $ \st yld sng stp ->
804 let single i = foldStreamShared st yld sng stp $ a |: yield i
805 yieldk i x = foldStreamShared st yld sng stp $ a |: return i |: go x
806 in foldStream st yieldk single stp m2
808{-# INLINE insertBy #-}
809insertBy :: IsStream t => (a -> a -> Ordering) -> a -> t m a -> t m a
810insertBy cmp x m = go m
812 go m1 = mkStream $ \st yld _ _ ->
813 let single a = case cmp x a of
814 GT -> yld a (yield x)
817 yieldk a r = case cmp x a of
819 _ -> yld x (a `cons` r)
820 in foldStream st yieldk single stop m1
822------------------------------------------------------------------------------
824------------------------------------------------------------------------------
826{-# INLINE deleteBy #-}
827deleteBy :: IsStream t => (a -> a -> Bool) -> a -> t m a -> t m a
828deleteBy eq x m = go m
830 go m1 = mkStream $ \st yld sng stp ->
831 let single a = if eq x a then stp else sng a
832 yieldk a r = if eq x a
833 then foldStream st yld sng stp r
835 in foldStream st yieldk single stp m1
837-------------------------------------------------------------------------------
839-------------------------------------------------------------------------------
841{-# INLINE mapMaybe #-}
842mapMaybe :: IsStream t => (a -> Maybe b) -> t m a -> t m b
845 go m1 = mkStream $ \st yld sng stp ->
846 let single a = case f a of
849 yieldk a r = case f a of
850 Just b -> yld b $ go r
851 Nothing -> foldStream (adaptState st) yieldk single stp r
852 in foldStream (adaptState st) yieldk single stp m1
854------------------------------------------------------------------------------
856------------------------------------------------------------------------------
858-- | Zip two streams serially using a pure zipping function.
861{-# INLINABLE zipWith #-}
862zipWith :: IsStream t => (a -> b -> c) -> t m a -> t m b -> t m c
865 go mx my = mkStream $ \st yld sng stp -> do
867 let single2 b = sng (f a b)
868 yield2 b rb = yld (f a b) (go ra rb)
869 in foldStream (adaptState st) yield2 single2 stp my
870 let single1 a = merge a nil
872 foldStream (adaptState st) yield1 single1 stp mx
874-- | Zip two streams serially using a monadic zipping function.
877zipWithM :: (IsStream t, Monad m) => (a -> b -> m c) -> t m a -> t m b -> t m c
878zipWithM f m1 m2 = go m1 m2
880 go mx my = mkStream $ \st yld sng stp -> do
882 let runIt x = foldStream st yld sng stp x
883 single2 b = f a b >>= sng
884 yield2 b rb = f a b >>= \x -> runIt (x `cons` go ra rb)
885 in foldStream (adaptState st) yield2 single2 stp my
886 let single1 a = merge a nil
888 foldStream (adaptState st) yield1 single1 stp mx
890------------------------------------------------------------------------------
892------------------------------------------------------------------------------
894{-# INLINE mergeByM #-}
896 :: (IsStream t, Monad m)
897 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
900 go mx my = mkStream $ \st yld sng stp -> do
901 let mergeWithY a ra =
902 let stop2 = foldStream st yld sng stp mx
906 GT -> yld b (go (a `cons` ra) nil)
907 _ -> yld a (go ra (b `cons` nil))
911 GT -> yld b (go (a `cons` ra) rb)
912 _ -> yld a (go ra (b `cons` rb))
913 in foldStream st yield2 single2 stop2 my
914 let stopX = foldStream st yld sng stp my
915 singleX a = mergeWithY a nil
917 foldStream st yieldX singleX stopX mx
919{-# INLINABLE mergeBy #-}
921 :: (IsStream t, Monad m)
922 => (a -> a -> Ordering) -> t m a -> t m a -> t m a
923mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
925------------------------------------------------------------------------------
926-- Transformation comprehensions
927------------------------------------------------------------------------------
930the :: (Eq a, IsStream t, Monad m) => t m a -> m (Maybe a)
934 Nothing -> return Nothing
935 Just (h, t) -> go h t
938 let single a | h == a = return $ Just h
939 | otherwise = return Nothing
940 yieldk a r | h == a = go h r
941 | otherwise = return Nothing
942 in foldStream defState yieldk single (return $ Just h) m1
944-------------------------------------------------------------------------------
946-------------------------------------------------------------------------------
948{-# INLINE bindWith #-}
951 => (forall c. t m c -> t m c -> t m c)
955bindWith par m1 f = go m1
958 mkStream $ \st yld sng stp ->
959 let foldShared = foldStreamShared st yld sng stp
960 single a = foldShared $ unShare (f a)
961 yieldk a r = foldShared $ unShare (f a) `par` go r
962 in foldStream (adaptState st) yieldk single stp m
964------------------------------------------------------------------------------
965-- Alternative & MonadPlus
966------------------------------------------------------------------------------
968_alt :: Stream m a -> Stream m a -> Stream m a
969_alt m1 m2 = mkStream $ \st yld sng stp ->
970 let stop = foldStream st yld sng stp m2
971 in foldStream st yld sng stop m1
973------------------------------------------------------------------------------
975------------------------------------------------------------------------------
977withLocal :: MonadReader r m => (r -> r) -> Stream m a -> Stream m a
979 mkStream $ \st yld sng stp ->
980 let single = local f . sng
981 yieldk a r = local f $ yld a (withLocal f r)
982 in foldStream st yieldk single (local f stp) m
984------------------------------------------------------------------------------
986------------------------------------------------------------------------------
989-- XXX handle and test cross thread state transfer
992 => Stream m a -> (e -> Stream m a) -> Stream m a
994 mkStream $ \_ stp sng yld ->
995 let run x = unStream x Nothing stp sng yieldk
996 handle r = r `catchError` \e -> run $ h e
997 yieldk a r = yld a (withCatchError r h)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list