endEnvironment :: HandleTeXIO m => LexerT m [Token]
endEnvironment = do
  name <- envName
  let endEnv = mkCtrlSeq "end": name
  grp <- getGroup <$> getState
  case grp of
    (DefinedGroup name' _ endCode) ->
      if null endCode
      then do -- close group
        modifyState . popGroup $
          DefinedGroup (stripBraces name) [] []
        return []
      else do -- inject end code
        modifyState . setGroup $
          DefinedGroup name' [] []
        prependTokens (endCode ++ endEnv)
        return []
    _ -> -- stack group is not a defined group
      if getGroupName grp == stripBraces name
      then do
        modifyState (popGroup (NamedGroup (stripBraces name)))
        return endEnv
      else do
        -- There is a mismatch between the stream group and the stack group.
        -- This may happen if the start code and end code portions of a
        -- newenvironment definition span named groups themselves.
        -- We attempt to find the stream group as a defined group in the
        -- full lexer stack and inject its end code into the stream.
        let definedGroup = DefinedGroup (stripBraces name) [] []
        endCode <- getGroupEndCode definedGroup <$> getState
        modifyState (clearGroup definedGroup)
        prependTokens (endCode ++ endEnv)
        return []

-- Parse the name of a LaTeX environment, including group delimiters.
--
-- We currently allow arbitrary token lists.
endEnvironment :: HandleTeXIO m => LexerT m [Token]
endEnvironment = do
  name <- envName
  let endEnv = mkCtrlSeq "end": name
  grp <- getGroup <$> getState
  case grp of
    (DefinedGroup name' _ endCode) ->
      if null endCode
      then do -- close group
        modifyState . popGroup $
          DefinedGroup (stripBraces name) [] []
        return []
      else do -- inject end code
        modifyState . setGroup $
          DefinedGroup name' [] []
        prependTokens (endCode ++ endEnv)
        return []
    _ -> -- stack group is not a defined group
      if getGroupName grp == stripBraces name
      then do
        modifyState (popGroup (NamedGroup (stripBraces name)))
        return endEnv
      else do
        -- There is a mismatch between the stream group and the stack group.
        -- This may happen if the start code and end code portions of a
        -- newenvironment definition span named groups themselves.
        -- We attempt to find the stream group as a defined group in the
        -- full lexer stack and inject its end code into the stream.
        let definedGroup = DefinedGroup (stripBraces name) [] []
        endCode <- getGroupEndCode definedGroup <$> getState
        modifyState (clearGroup definedGroup)
        prependTokens (endCode ++ endEnv)
        return []

-- Parse the name of a LaTeX environment, including group delimiters.
--
-- We currently allow arbitrary token lists.
endEnvironment = do
  name <- envName
  let endEnv = mkCtrlSeq "end": name
  grp <- getGroup <$> getState
  case grp of
    (DefinedGroup name' _ endCode) ->
      if null endCode
      then do -- close group
        modifyState . popGroup $
          DefinedGroup (stripBraces name) [] []
        return []
      else do -- inject end code
        modifyState . setGroup $
          DefinedGroup name' [] []
        prependTokens (endCode ++ endEnv)
        return []
    _ -> -- stack group is not a defined group
      if getGroupName grp == stripBraces name
      then do
        modifyState (popGroup (NamedGroup (stripBraces name)))
        return endEnv
      else do
        -- There is a mismatch between the stream group and the stack group.
        -- This may happen if the start code and end code portions of a
        -- newenvironment definition span named groups themselves.
        -- We attempt to find the stream group as a defined group in the
        -- full lexer stack and inject its end code into the stream.
        let definedGroup = DefinedGroup (stripBraces name) [] []
        endCode <- getGroupEndCode definedGroup <$> getState
        modifyState (clearGroup definedGroup)
        prependTokens (endCode ++ endEnv)
        return []

-- Parse the name of a LaTeX environment, including group delimiters.
--
-- We currently allow arbitrary token lists.