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
96 -- ** Specialized Folds
125 -- ** By folding (scans)
168 -- ** Transformation comprehensions
171 -- * Semigroup Style Composition
179 , Streaming -- deprecated
184import Control.Monad.Trans (MonadTrans(lift))
185import Control.Monad (void)
186import Control.Monad.Reader.Class (MonadReader(..))
188 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
189 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
190 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
191 foldr1, (!!), replicate, reverse, concatMap)
192import qualified Prelude
195import Streamly.Streams.StreamK.Type
197-------------------------------------------------------------------------------
199-------------------------------------------------------------------------------
202uncons :: (IsStream t, Monad m) => t m a -> m (Maybe (a, t m a))
204 let stop = return Nothing
205 single a = return (Just (a, nil))
206 yieldk a r = return (Just (a, r))
207 in foldStream defState yieldk single stop m
209-------------------------------------------------------------------------------
211-------------------------------------------------------------------------------
213{-# INLINE unfoldr #-}
214unfoldr :: IsStream t => (b -> Maybe (a, b)) -> b -> t m a
217 go s = buildS $ \yld stp ->
219 Just (a, b) -> yld a (go b)
222{-# INLINE unfoldrM #-}
223unfoldrM :: (IsStream t, MonadAsync m) => (b -> m (Maybe (a, b))) -> b -> t m a
226 go s = sharedM $ \yld _ stp -> do
229 Just (a, b) -> yld a (go b)
233-- Generalization of concurrent streams/SVar via unfoldr.
235-- Unfold a value into monadic actions and then run the resulting monadic
236-- actions to generate a stream. Since the step of generating the monadic
237-- action and running them are decoupled we can run the monadic actions
238-- cooncurrently. For example, the seed could be a list of monadic actions or a
239-- pure stream of monadic actions.
241-- We can have different flavors of this depending on the stream type t. The
242-- concurrent version could be async or ahead etc. Depending on how we queue
243-- back the feedback portion b, it could be DFS or BFS style.
245unfoldrA :: (IsStream t, MonadAsync m) => (b -> Maybe (m a, b)) -> b -> t m a
249-------------------------------------------------------------------------------
251-------------------------------------------------------------------------------
256{-# DEPRECATED once "Please use yieldM instead." #-}
258once :: (Monad m, IsStream t) => m a -> t m a
263-- repeatM = fix . cons
264-- repeatM = cycle1 . yield
267-- Generate an infinite stream by repeating a pure value.
271repeat :: IsStream t => a -> t m a
272repeat a = let x = cons a x in x
274{-# INLINE replicateM #-}
275replicateM :: (IsStream t, MonadAsync m) => Int -> m a -> t m a
278 go cnt = if cnt <= 0 then nil else m |: go (cnt - 1)
280{-# INLINE replicate #-}
281replicate :: IsStream t => Int -> a -> t m a
284 go cnt = if cnt <= 0 then nil else a `cons` go (cnt - 1)
286-------------------------------------------------------------------------------
288-------------------------------------------------------------------------------
292-- fromFoldable = 'Prelude.foldr' 'cons' 'nil'
295-- Construct a stream from a 'Foldable' containing pure values:
298{-# INLINE fromFoldable #-}
299fromFoldable :: (IsStream t, Foldable f) => f a -> t m a
300fromFoldable = Prelude.foldr cons nil
302{-# INLINE fromList #-}
303fromList :: IsStream t => [a] -> t m a
304fromList = fromFoldable
306{-# INLINE fromStreamK #-}
307fromStreamK :: IsStream t => Stream m a -> t m a
308fromStreamK = fromStream
310-------------------------------------------------------------------------------
311-- Elimination by Folding
312-------------------------------------------------------------------------------
314-- | Lazy right associative fold.
316foldr :: (IsStream t, Monad m) => (a -> b -> b) -> b -> t m a -> m b
317foldr step acc = foldrM (\x xs -> xs >>= \b -> return (step x b)) (return acc)
319-- | Right associative fold to an arbitrary transformer monad.
321foldrT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
322 => (a -> s m b -> s m b) -> s m b -> t m a -> s m b
323foldrT step final m = go m
326 res <- lift $ uncons m1
328 Just (h, t) -> step h (go t)
332foldr1 :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> m (Maybe a)
336 Nothing -> return Nothing
337 Just (h, t) -> fmap Just (go h t)
341 single a = return $ step a p
342 yieldk a r = fmap (step p) (go a r)
343 in foldStream defState yieldk single stp m1
345-- | Strict left fold with an extraction function. Like the standard strict
346-- left fold, but applies a user supplied extraction function (the third
347-- argument) to the folded value at the end. This is designed to work with the
348-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
350-- Note that the accumulator is always evaluated including the initial value.
351{-# INLINE foldlx' #-}
352foldlx' :: forall t m a b x. (IsStream t, Monad m)
353 => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
354foldlx' step begin done m = get $ go m begin
359 -- XXX we are not strictly evaluating the accumulator here. Is this
361 let single = return . done
362 -- XXX this is foldSingleton. why foldStreamShared?
363 in foldStreamShared undefined undefined single undefined m1
365 -- Note, this can be implemented by making a recursive call to "go",
366 -- however that is more expensive because of unnecessary recursion
367 -- that cannot be tail call optimized. Unfolding recursion explicitly via
368 -- continuations is much more efficient.
369 go :: t m a -> x -> t m x
370 go m1 !acc = mkStream $ \_ yld sng _ ->
372 single a = sng $ step acc a
373 -- XXX this is foldNonEmptyStream
374 yieldk a r = foldStream defState yld sng undefined $
376 in foldStream defState yieldk single stop m1
378-- | Strict left associative fold.
380foldl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> m b
381foldl' step begin = foldlx' step begin id
383-- XXX replace the recursive "go" with explicit continuations.
384-- | Like 'foldx', but with a monadic step function.
385{-# INLINABLE foldlMx' #-}
386foldlMx' :: (IsStream t, Monad m)
387 => (x -> a -> m x) -> m x -> (x -> m b) -> t m a -> m b
388foldlMx' step begin done m = go begin m
391 let stop = acc >>= done
392 single a = acc >>= \b -> step b a >>= done
393 yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r
394 in foldStream defState yieldk single stop m1
396-- | Like 'foldl'' but with a monadic step function.
397{-# INLINE foldlM' #-}
398foldlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> m b
399foldlM' step begin = foldlMx' step (return begin) return
401-- | Lazy left fold to a stream.
403foldlS :: IsStream t => (t m b -> a -> t m b) -> t m b -> t m a -> t m b
404foldlS step begin m = go begin m
406 go acc rest = mkStream $ \st yld sng stp ->
407 let run x = foldStream st yld sng stp x
409 single a = run $ step acc a
410 yieldk a r = run $ go (step acc a) r
411 in foldStream (adaptState st) yieldk single stop rest
413-- | Lazy left fold to an arbitrary transformer monad.
415foldlT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
416 => (s m b -> a -> s m b) -> s m b -> t m a -> s m b
417foldlT step begin m = go begin m
420 res <- lift $ uncons m1
422 Just (h, t) -> go (step acc h) t
425------------------------------------------------------------------------------
427------------------------------------------------------------------------------
429-- XXX use foldrM to implement folds where possible
430-- XXX This (commented) definition of drain and mapM_ perform much better on
431-- some benchmarks but worse on others. Need to investigate why, may there is
432-- an optimization opportunity that we can exploit.
433-- drain = foldrM (\_ xs -> return () >> xs) (return ())
436-- > drain = foldl' (\_ _ -> ()) ()
437-- > drain = mapM_ (\_ -> return ())
439drain :: (Monad m, IsStream t) => t m a -> m ()
440drain = foldrM (\_ xs -> xs) (return ())
448 in foldStream defState yieldk single stop m1
452null :: (IsStream t, Monad m) => t m a -> m Bool
453-- null = foldrM (\_ _ -> return True) (return False)
455 let stop = return True
456 single _ = return False
457 yieldk _ _ = return False
458 in foldStream defState yieldk single stop m
461head :: (IsStream t, Monad m) => t m a -> m (Maybe a)
462-- head = foldrM (\x _ -> return $ Just x) (return Nothing)
464 let stop = return Nothing
465 single a = return (Just a)
466 yieldk a _ = return (Just a)
467 in foldStream defState yieldk single stop m
470tail :: (IsStream t, Monad m) => t m a -> m (Maybe (t m a))
472 let stop = return Nothing
473 single _ = return $ Just nil
474 yieldk _ r = return $ Just r
475 in foldStream defState yieldk single stop m
478init :: (IsStream t, Monad m) => t m a -> m (Maybe (t m a))
484 Nothing -> return Nothing
485 Just (h, t) -> return . Just $ go h t
486 go p m1 = mkStream $ \_ yld sng stp ->
488 yieldk a x = yld p $ go a x
489 in foldStream defState yieldk single stp m1
492elem :: (IsStream t, Monad m, Eq a) => a -> t m a -> m Bool
496 let stop = return False
497 single a = return (a == e)
498 yieldk a r = if a == e then return True else go r
499 in foldStream defState yieldk single stop m1
501{-# INLINE notElem #-}
502notElem :: (IsStream t, Monad m, Eq a) => a -> t m a -> m Bool
506 let stop = return True
507 single a = return (a /= e)
508 yieldk a r = if a == e then return False else go r
509 in foldStream defState yieldk single stop m1
512all :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> m Bool
516 let single a | p a = return True
517 | otherwise = return False
518 yieldk a r | p a = go r
519 | otherwise = return False
520 in foldStream defState yieldk single (return True) m1
523any :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> m Bool
527 let single a | p a = return True
528 | otherwise = return False
529 yieldk a r | p a = return True
531 in foldStream defState yieldk single (return False) m1
533-- | Extract the last element of the stream, if any.
535last :: (IsStream t, Monad m) => t m a -> m (Maybe a)
536last = foldlx' (\_ y -> Just y) Nothing id
538{-# INLINE minimum #-}
539minimum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
540minimum m = go Nothing m
543 let stop = return Nothing
544 single a = return (Just a)
545 yieldk a r = go (Just a) r
546 in foldStream defState yieldk single stop m1
549 let stop = return (Just res)
552 then return (Just res)
558 in foldStream defState yieldk single stop m1
560{-# INLINE minimumBy #-}
562 :: (IsStream t, Monad m)
563 => (a -> a -> Ordering) -> t m a -> m (Maybe a)
564minimumBy cmp m = go Nothing m
567 let stop = return Nothing
568 single a = return (Just a)
569 yieldk a r = go (Just a) r
570 in foldStream defState yieldk single stop m1
573 let stop = return (Just res)
574 single a = case cmp res a of
575 GT -> return (Just a)
576 _ -> return (Just res)
577 yieldk a r = case cmp res a of
580 in foldStream defState yieldk single stop m1
582{-# INLINE maximum #-}
583maximum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
584maximum m = go Nothing m
587 let stop = return Nothing
588 single a = return (Just a)
589 yieldk a r = go (Just a) r
590 in foldStream defState yieldk single stop m1
593 let stop = return (Just res)
597 else return (Just res)
602 in foldStream defState yieldk single stop m1
604{-# INLINE maximumBy #-}
605maximumBy :: (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> m (Maybe a)
606maximumBy cmp m = go Nothing m
609 let stop = return Nothing
610 single a = return (Just a)
611 yieldk a r = go (Just a) r
612 in foldStream defState yieldk single stop m1
615 let stop = return (Just res)
616 single a = case cmp res a of
617 GT -> return (Just res)
619 yieldk a r = case cmp res a of
620 GT -> go (Just res) r
622 in foldStream defState yieldk single stop m1
625(!!) :: (IsStream t, Monad m) => t m a -> Int -> m (Maybe a)
629 let single a | n == 0 = return $ Just a
630 | otherwise = return Nothing
631 yieldk a x | n < 0 = return Nothing
632 | n == 0 = return $ Just a
633 | otherwise = go (n - 1) x
634 in foldStream defState yieldk single (return Nothing) m1
637lookup :: (IsStream t, Monad m, Eq a) => a -> t m (a, b) -> m (Maybe b)
641 let single (a, b) | a == e = return $ Just b
642 | otherwise = return Nothing
643 yieldk (a, b) x | a == e = return $ Just b
645 in foldStream defState yieldk single (return Nothing) m1
648findM :: (IsStream t, Monad m) => (a -> m Bool) -> t m a -> m (Maybe a)
654 if b then return $ Just a else return Nothing
657 if b then return $ Just a else go x
658 in foldStream defState yieldk single (return Nothing) m1
661find :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> m (Maybe a)
662find p = findM (return . p)
664{-# INLINE findIndices #-}
665findIndices :: IsStream t => (a -> Bool) -> t m a -> t m Int
668 go offset m1 = mkStream $ \st yld sng stp ->
669 let single a | p a = sng offset
671 yieldk a x | p a = yld offset $ go (offset + 1) x
672 | otherwise = foldStream (adaptState st) yld sng stp $
674 in foldStream (adaptState st) yieldk single stp m1
676------------------------------------------------------------------------------
678------------------------------------------------------------------------------
680-- | Apply a monadic action to each element of the stream and discard the
681-- output of the action.
683mapM_ :: (IsStream t, Monad m) => (a -> m b) -> t m a -> m ()
688 single a = void (f a)
689 yieldk a r = f a >> go r
690 in foldStream defState yieldk single stop m1
692------------------------------------------------------------------------------
694------------------------------------------------------------------------------
696{-# INLINABLE toList #-}
697toList :: (IsStream t, Monad m) => t m a -> m [a]
700{-# INLINE toStreamK #-}
701toStreamK :: Stream m a -> Stream m a
704-------------------------------------------------------------------------------
705-- Transformation by folding (Scans)
706-------------------------------------------------------------------------------
708{-# INLINE scanlx' #-}
709scanlx' :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
710scanlx' step begin done m =
711 cons (done begin) $ go m begin
713 go m1 !acc = mkStream $ \st yld sng stp ->
714 let single a = sng (done $ step acc a)
717 in yld (done s) (go r s)
718 in foldStream (adaptState st) yieldk single stp m1
721scanl' :: IsStream t => (b -> a -> b) -> b -> t m a -> t m b
722scanl' step begin = scanlx' step begin id
724-------------------------------------------------------------------------------
726-------------------------------------------------------------------------------
729filter :: IsStream t => (a -> Bool) -> t m a -> t m a
732 go m1 = mkStream $ \st yld sng stp ->
733 let single a | p a = sng a
735 yieldk a r | p a = yld a (go r)
736 | otherwise = foldStream st yieldk single stp r
737 in foldStream st yieldk single stp m1
740take :: IsStream t => Int -> t m a -> t m a
743 go n1 m1 = mkStream $ \st yld sng stp ->
744 let yieldk a r = yld a (go (n1 - 1) r)
747 else foldStream st yieldk sng stp m1
749{-# INLINE takeWhile #-}
750takeWhile :: IsStream t => (a -> Bool) -> t m a -> t m a
753 go m1 = mkStream $ \st yld sng stp ->
754 let single a | p a = sng a
756 yieldk a r | p a = yld a (go r)
758 in foldStream st yieldk single stp m1
761drop :: IsStream t => Int -> t m a -> t m a
762drop n m = fromStream $ unShare (go n (toStream m))
764 go n1 m1 = mkStream $ \st yld sng stp ->
766 yieldk _ r = foldStreamShared st yld sng stp $ go (n1 - 1) r
767 -- Somehow "<=" check performs better than a ">"
769 then foldStreamShared st yld sng stp m1
770 else foldStreamShared st yieldk single stp m1
772{-# INLINE dropWhile #-}
773dropWhile :: IsStream t => (a -> Bool) -> t m a -> t m a
776 go m1 = mkStream $ \st yld sng stp ->
777 let single a | p a = stp
779 yieldk a r | p a = foldStream st yieldk single stp r
780 | otherwise = yld a r
781 in foldStream st yieldk single stp m1
783-------------------------------------------------------------------------------
785-------------------------------------------------------------------------------
787-- Be careful when modifying this, this uses a consM (|:) deliberately to allow
788-- other stream types to overload it.
789{-# INLINE sequence #-}
790sequence :: (IsStream t, MonadAsync m) => t m (m a) -> t m a
793 go m1 = mkStream $ \st yld sng stp ->
794 let single ma = ma >>= sng
795 yieldk ma r = foldStreamShared st yld sng stp $ ma |: go r
796 in foldStream (adaptState st) yieldk single stp m1
798-------------------------------------------------------------------------------
800-------------------------------------------------------------------------------
802{-# INLINE intersperseM #-}
803intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
804intersperseM a m = prependingStart m
806 prependingStart m1 = mkStream $ \st yld sng stp ->
807 let yieldk i x = foldStreamShared st yld sng stp $ return i |: go x
808 in foldStream st yieldk sng stp m1
809 go m2 = mkStream $ \st yld sng stp ->
810 let single i = foldStreamShared st yld sng stp $ a |: yield i
811 yieldk i x = foldStreamShared st yld sng stp $ a |: return i |: go x
812 in foldStream st yieldk single stp m2
814{-# INLINE insertBy #-}
815insertBy :: IsStream t => (a -> a -> Ordering) -> a -> t m a -> t m a
816insertBy cmp x m = go m
818 go m1 = mkStream $ \st yld _ _ ->
819 let single a = case cmp x a of
820 GT -> yld a (yield x)
823 yieldk a r = case cmp x a of
825 _ -> yld x (a `cons` r)
826 in foldStream st yieldk single stop m1
828------------------------------------------------------------------------------
830------------------------------------------------------------------------------
832{-# INLINE deleteBy #-}
833deleteBy :: IsStream t => (a -> a -> Bool) -> a -> t m a -> t m a
834deleteBy eq x m = go m
836 go m1 = mkStream $ \st yld sng stp ->
837 let single a = if eq x a then stp else sng a
838 yieldk a r = if eq x a
839 then foldStream st yld sng stp r
841 in foldStream st yieldk single stp m1
843------------------------------------------------------------------------------
845------------------------------------------------------------------------------
847{-# INLINE reverse #-}
848reverse :: IsStream t => t m a -> t m a
849reverse = foldlS (flip cons) nil
851-------------------------------------------------------------------------------
853-------------------------------------------------------------------------------
855{-# INLINE mapMaybe #-}
856mapMaybe :: IsStream t => (a -> Maybe b) -> t m a -> t m b
859 go m1 = mkStream $ \st yld sng stp ->
860 let single a = case f a of
863 yieldk a r = case f a of
864 Just b -> yld b $ go r
865 Nothing -> foldStream (adaptState st) yieldk single stp r
866 in foldStream (adaptState st) yieldk single stp m1
868------------------------------------------------------------------------------
870------------------------------------------------------------------------------
872-- | Zip two streams serially using a pure zipping function.
875{-# INLINABLE zipWith #-}
876zipWith :: IsStream t => (a -> b -> c) -> t m a -> t m b -> t m c
879 go mx my = mkStream $ \st yld sng stp -> do
881 let single2 b = sng (f a b)
882 yield2 b rb = yld (f a b) (go ra rb)
883 in foldStream (adaptState st) yield2 single2 stp my
884 let single1 a = merge a nil
886 foldStream (adaptState st) yield1 single1 stp mx
888-- | Zip two streams serially using a monadic zipping function.
891{-# INLINABLE zipWithM #-}
892zipWithM :: (IsStream t, Monad m) => (a -> b -> m c) -> t m a -> t m b -> t m c
893zipWithM f m1 m2 = go m1 m2
895 go mx my = mkStream $ \st yld sng stp -> do
897 let runIt x = foldStream st yld sng stp x
898 single2 b = f a b >>= sng
899 yield2 b rb = f a b >>= \x -> runIt (x `cons` go ra rb)
900 in foldStream (adaptState st) yield2 single2 stp my
901 let single1 a = merge a nil
903 foldStream (adaptState st) yield1 single1 stp mx
905------------------------------------------------------------------------------
907------------------------------------------------------------------------------
909{-# INLINE mergeByM #-}
911 :: (IsStream t, Monad m)
912 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
915 go mx my = mkStream $ \st yld sng stp -> do
916 let mergeWithY a ra =
917 let stop2 = foldStream st yld sng stp mx
921 GT -> yld b (go (a `cons` ra) nil)
922 _ -> yld a (go ra (b `cons` nil))
926 GT -> yld b (go (a `cons` ra) rb)
927 _ -> yld a (go ra (b `cons` rb))
928 in foldStream st yield2 single2 stop2 my
929 let stopX = foldStream st yld sng stp my
930 singleX a = mergeWithY a nil
932 foldStream st yieldX singleX stopX mx
934{-# INLINABLE mergeBy #-}
936 :: (IsStream t, Monad m)
937 => (a -> a -> Ordering) -> t m a -> t m a -> t m a
938mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
940------------------------------------------------------------------------------
941-- Transformation comprehensions
942------------------------------------------------------------------------------
945the :: (Eq a, IsStream t, Monad m) => t m a -> m (Maybe a)
949 Nothing -> return Nothing
950 Just (h, t) -> go h t
953 let single a | h == a = return $ Just h
954 | otherwise = return Nothing
955 yieldk a r | h == a = go h r
956 | otherwise = return Nothing
957 in foldStream defState yieldk single (return $ Just h) m1
959------------------------------------------------------------------------------
960-- Alternative & MonadPlus
961------------------------------------------------------------------------------
963_alt :: Stream m a -> Stream m a -> Stream m a
964_alt m1 m2 = mkStream $ \st yld sng stp ->
965 let stop = foldStream st yld sng stp m2
966 in foldStream st yld sng stop m1
968------------------------------------------------------------------------------
970------------------------------------------------------------------------------
972{-# INLINABLE withLocal #-}
973withLocal :: MonadReader r m => (r -> r) -> Stream m a -> Stream m a
975 mkStream $ \st yld sng stp ->
976 let single = local f . sng
977 yieldk a r = local f $ yld a (withLocal f r)
978 in foldStream st yieldk single (local f stp) m
980------------------------------------------------------------------------------
982------------------------------------------------------------------------------
985-- XXX handle and test cross thread state transfer
988 => Stream m a -> (e -> Stream m a) -> Stream m a
990 mkStream $ \_ stp sng yld ->
991 let run x = unStream x Nothing stp sng yieldk
992 handle r = r `catchError` \e -> run $ h e
993 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