@@ -202,15 +202,54 @@ lexKeys baseMap ('\ESC':cs)
202202 = metaKey k : ks
203203lexKeys baseMap (c: cs) = simpleChar c : lexKeys baseMap cs
204204
205- lookupChars :: TreeMap Char Key -> [Char ] -> Maybe (Key ,[Char ])
206- lookupChars _ [] = Nothing
207- lookupChars (TreeMap tm) (c: cs) = case Map. lookup c tm of
208- Nothing -> Nothing
209- Just (Nothing ,t) -> lookupChars t cs
210- Just (Just k, t@ (TreeMap tm2))
211- | not (null cs) && not (Map. null tm2) -- ?? lookup d tm2?
212- -> lookupChars t cs
213- | otherwise -> Just (k, cs)
205+ -- | Walk @cs@ through the tree, returning the /longest/ key found along
206+ -- the path together with whatever bytes follow it.
207+ --
208+ -- If a longer match is attempted but doesn't pan out (the next byte
209+ -- isn't in the deeper subtree), we fall back to the most recent shorter
210+ -- key we passed. This avoids the well-known sharp edge where input
211+ -- like @\\ESC[AC@ — with @\\ESC[A@ registered (up-arrow) and the @A@
212+ -- node having children that don't include @C@ — would previously be
213+ -- reported as no match at all, instead of @up-arrow + 'C'@.
214+ --
215+ -- 'Nothing' is returned only when no key is reached anywhere along the
216+ -- walk.
217+ lookupChars :: TreeMap Char Key -> [Char ] -> Maybe (Key , [Char ])
218+ lookupChars _ [] = Nothing
219+ lookupChars tree cs = go Nothing tree cs
220+ where
221+ -- 'best' holds the longest committed match so far (or Nothing if
222+ -- we haven't passed a key node yet). When the next byte doesn't
223+ -- extend the path we return 'best'.
224+ go best (TreeMap m) (c: cs') = case Map. lookup c m of
225+ Nothing -> best
226+ Just (mKey, sub) ->
227+ let best' = maybe best (\ k -> Just (k, cs')) mKey
228+ in case cs' of
229+ _: _ -> go best' sub cs'
230+ [] -> best'
231+ go best _ [] = best -- unreachable: top-level [] handled above
232+
233+ -- | Greedily peel complete keys off the front of @cs@. Returns the
234+ -- decoded keys (in forward order) and the leftover bytes that didn't
235+ -- (yet) form a complete tree match.
236+ peelCompleteKeys :: TreeMap Char Key -> [Char ] -> ([Key ], [Char ])
237+ peelCompleteKeys tree = go id
238+ where
239+ go acc cs = case lookupChars tree cs of
240+ Just (k, rest) -> go (acc . (k: )) rest
241+ Nothing -> (acc [] , cs)
242+
243+ -- | True if @cs@ matches a /strict/ (non-terminal) prefix of some path
244+ -- in the @TreeMap@ — i.e. it could still be extended into a complete
245+ -- key sequence if more bytes arrive.
246+ isStrictTreePrefix :: TreeMap Char b -> [Char ] -> Bool
247+ isStrictTreePrefix _ [] = False
248+ isStrictTreePrefix (TreeMap m) (c: cs) = case Map. lookup c m of
249+ Nothing -> False
250+ Just (_, sub@ (TreeMap sm)) -> case cs of
251+ [] -> not (Map. null sm)
252+ _ -> isStrictTreePrefix sub cs
214253
215254-----------------------------
216255
@@ -219,8 +258,9 @@ withPosixGetEvent :: (MonadIO m, MonadMask m, MonadReader Prefs m)
219258 -> (m Event -> m a ) -> m a
220259withPosixGetEvent eventChan h termKeys f = wrapTerminalOps h $ do
221260 baseMap <- getKeySequences (ehIn h) termKeys
261+ timeoutMs <- asks (fromIntegral . keyseqTimeoutMs :: Prefs -> Int )
222262 withWindowHandler eventChan
223- $ f $ liftIO $ getEvent (ehIn h) baseMap eventChan
263+ $ f $ liftIO $ getEvent (ehIn h) timeoutMs baseMap eventChan
224264
225265withWindowHandler :: (MonadIO m , MonadMask m ) => TChan Event -> m a -> m a
226266withWindowHandler eventChan = withHandler windowChange $
@@ -238,27 +278,62 @@ withHandler signal handler f = do
238278 old_handler <- liftIO $ installHandler signal handler Nothing
239279 f `finally` liftIO (installHandler signal old_handler Nothing )
240280
241- getEvent :: Handle -> TreeMap Char Key -> TChan Event -> IO Event
242- getEvent h baseMap = keyEventLoop $ do
243- cs <- getBlockOfChars h
244- return [KeyInput $ lexKeys baseMap cs]
245-
246- -- Read at least one character of input, and more if immediately
247- -- available. In particular the characters making up a control sequence
248- -- will all be available at once, so they can be processed together
249- -- (with Posix.lexKeys).
250- getBlockOfChars :: Handle -> IO String
251- getBlockOfChars h = do
281+ getEvent :: Handle -> Int -> TreeMap Char Key -> TChan Event -> IO Event
282+ getEvent h timeoutMs baseMap = keyEventLoop $ do
283+ ks <- getBlockOfKeys h baseMap timeoutMs
284+ return [KeyInput ks]
285+
286+ -- | Read at least one key of input, and more if available.
287+ --
288+ -- A multi-byte control sequence (e.g. @\\ESC[A@ for arrow-up) may not
289+ -- arrive in a single read on a slow link such as a low-bandwidth serial
290+ -- port: the @\\ESC@ can land before the @[A@. When the buffer ends in
291+ -- bytes that are a strict prefix of some known key sequence, we wait up
292+ -- to @timeoutMs@ for the rest before returning. Otherwise we return as
293+ -- soon as nothing more is buffered. This mirrors GNU Readline's
294+ -- @keyseq-timeout@.
295+ --
296+ -- The loop tracks bytes and decoded keys side by side: every time we
297+ -- run out of immediately-available input we peel complete keys from the
298+ -- front of the byte buffer and shrink it down to just its trailing
299+ -- (possibly partial) prefix. That keeps the per-decision work bounded
300+ -- by the size of the in-flight sequence rather than the whole accrued
301+ -- buffer, which matters when bytes trickle in slowly.
302+ --
303+ -- Win32 is unaffected: that backend reads structured @INPUT_RECORD@
304+ -- key events rather than raw bytes, so escape sequences are never
305+ -- fragmented in the first place.
306+ getBlockOfKeys :: Handle -> TreeMap Char Key -> Int -> IO [Key ]
307+ getBlockOfKeys h baseMap timeoutMs = do
252308 c <- hGetChar h
253- loop [c]
309+ loop [c] []
254310 where
255- loop cs = do
256- isReady <- hReady h
257- if not isReady
258- then return $ reverse cs
259- else do
260- c <- hGetChar h
261- loop (c: cs)
311+ -- Both lists hold values in reverse (newest first) so that consing a
312+ -- new element is O(1). We reverse only at decision points or on
313+ -- return.
314+ loop bytesRev keysRev = do
315+ ready <- hWaitForInput h 0
316+ if ready
317+ then do
318+ c <- hGetChar h
319+ loop (c: bytesRev) keysRev
320+ else
321+ -- Drain whatever complete keys are sitting at the head of
322+ -- the byte buffer; what's left is either empty (done) or a
323+ -- still-in-flight partial sequence.
324+ let (peeled, partial) = peelCompleteKeys baseMap (reverse bytesRev)
325+ keysRev' = reverse peeled ++ keysRev
326+ in if null partial
327+ then pure (reverse keysRev')
328+ else if isStrictTreePrefix baseMap partial
329+ then do
330+ arrived <- hWaitForInput h timeoutMs
331+ if arrived
332+ then do
333+ c <- hGetChar h
334+ loop (c : reverse partial) keysRev'
335+ else pure (reverse keysRev' ++ lexKeys baseMap partial)
336+ else pure (reverse keysRev' ++ lexKeys baseMap partial)
262337
263338stdinTTYHandles , ttyHandles :: MaybeT IO Handles
264339stdinTTYHandles = do
0 commit comments