From 1688f1e40745459f397038340a0e2b6d15e53335 Mon Sep 17 00:00:00 2001 From: Karl Ostmo Date: Sun, 30 Jun 2024 17:12:01 -0700 Subject: [PATCH] Use 'enumerate' from the 'extra' package (#2003) Use [`enumerate`](https://hackage.haskell.org/package/extra-1.7.16/docs/Data-List-Extra.html#v:enumerate) instead of defining `listEnums` ourselves. Also discovered a "safer" implementation of nonempty enumerations, which lets of remove the `Data.List.NonEmpty.fromList` hlint exclusion. --- .hlint.yaml | 1 - src/swarm-doc/Swarm/Doc/Command.hs | 4 ++-- src/swarm-doc/Swarm/Doc/Gen.hs | 5 +++-- src/swarm-doc/Swarm/Doc/Wiki/Cheatsheet.hs | 5 +++-- src/swarm-engine/Swarm/Game/Step/Util/Inspect.hs | 4 ++-- src/swarm-lang/Swarm/Language/Parser/Lex.hs | 5 +++-- src/swarm-lang/Swarm/Language/Parser/Type.hs | 4 ++-- src/swarm-lang/Swarm/Language/Syntax/Constants.hs | 8 ++++---- .../Swarm/Game/Achievement/Definitions.hs | 6 +++--- src/swarm-topography/Swarm/Game/Location.hs | 7 ++++--- src/swarm-tui/Swarm/TUI/Controller.hs | 3 ++- src/swarm-tui/Swarm/TUI/Editor/Model.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Launch/Controller.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Launch/Prep.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Model/Goal.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Model/Menu.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Model/StateUpdate.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Model/Structure.hs | 4 ++-- src/swarm-tui/Swarm/TUI/Model/UI.hs | 3 ++- src/swarm-tui/Swarm/TUI/View.hs | 3 ++- src/swarm-util/Swarm/Language/Syntax/Direction.hs | 8 ++++---- src/swarm-util/Swarm/Util.hs | 15 ++++++--------- 22 files changed, 56 insertions(+), 53 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index f63280779..048ca5232 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -27,7 +27,6 @@ - functions: - {name: Data.List.head, within: []} - {name: Prelude.head, within: [Swarm.Web.Tournament.Database.Query]} - - {name: Data.List.NonEmpty.fromList, within: [Swarm.Util]} - {name: Prelude.tail, within: []} - {name: Prelude.!!, within: [Swarm.Util.indexWrapNonEmpty, TestEval]} - {name: undefined, within: [Swarm.Language.Key, TestUtil]} diff --git a/src/swarm-doc/Swarm/Doc/Command.hs b/src/swarm-doc/Swarm/Doc/Command.hs index 856a46dfc..7452f2cce 100644 --- a/src/swarm-doc/Swarm/Doc/Command.hs +++ b/src/swarm-doc/Swarm/Doc/Command.hs @@ -5,6 +5,7 @@ module Swarm.Doc.Command where import Data.Aeson (ToJSON) +import Data.List.Extra (enumerate) import Data.List.NonEmpty qualified as NE import Data.Set (Set) import Data.Set qualified as Set @@ -16,7 +17,6 @@ import Swarm.Language.Syntax import Swarm.Language.Syntax.CommandMetadata import Swarm.Language.Typecheck (inferConst) import Swarm.Language.Types -import Swarm.Util (listEnums) data DerivedAttrs = DerivedAttrs { hasActorTarget :: Bool @@ -54,7 +54,7 @@ mkEntry c = { hasActorTarget = operatesOnActor inputArgs , pureComputation = Set.null cmdEffects , modifiesEnvironment = Mutation EntityChange `Set.member` cmdEffects - , modifiesRobot = not . Set.disjoint cmdEffects . Set.fromList $ map (Mutation . RobotChange) listEnums + , modifiesRobot = not . Set.disjoint cmdEffects . Set.fromList $ map (Mutation . RobotChange) enumerate , movesRobot = Mutation (RobotChange PositionChange) `Set.member` cmdEffects , returnsValue = theOutputType /= TyCmd TyUnit , outputType = show theOutputType diff --git a/src/swarm-doc/Swarm/Doc/Gen.hs b/src/swarm-doc/Swarm/Doc/Gen.hs index b40dec580..86572ed57 100644 --- a/src/swarm-doc/Swarm/Doc/Gen.hs +++ b/src/swarm-doc/Swarm/Doc/Gen.hs @@ -20,6 +20,7 @@ import Control.Lens (view, (^.)) import Control.Monad (zipWithM, zipWithM_) import Data.Containers.ListUtils (nubOrd) import Data.Foldable (toList) +import Data.List.Extra (enumerate) import Data.Map.Lazy (Map, (!)) import Data.Map.Lazy qualified as Map import Data.Maybe (fromMaybe, mapMaybe) @@ -43,7 +44,7 @@ import Swarm.Game.Scenario (GameStateInputs (..), ScenarioInputs (..), loadStand import Swarm.Game.World.Gen (extractEntities) import Swarm.Game.World.Typecheck (Some (..), TTerm) import Swarm.Language.Key (specialKeyNames) -import Swarm.Util (both, listEnums) +import Swarm.Util (both) import Text.Dot (Dot, NodeId, (.->.)) import Text.Dot qualified as Dot @@ -83,7 +84,7 @@ generateDocs = \case putStrLn $ "-- " <> show et putStrLn $ replicate 40 '-' generateEditorKeywords et - mapM_ editorGen listEnums + mapM_ editorGen enumerate SpecialKeyNames -> generateSpecialKeyNames CheatSheet address s -> makeWikiPage address s TutorialCoverage -> renderTutorialProgression >>= putStrLn . T.unpack diff --git a/src/swarm-doc/Swarm/Doc/Wiki/Cheatsheet.hs b/src/swarm-doc/Swarm/Doc/Wiki/Cheatsheet.hs index 44305c045..cae579946 100644 --- a/src/swarm-doc/Swarm/Doc/Wiki/Cheatsheet.hs +++ b/src/swarm-doc/Swarm/Doc/Wiki/Cheatsheet.hs @@ -17,6 +17,7 @@ import Control.Lens (view, (^.)) import Control.Lens.Combinators (to) import Data.Foldable (find, toList) import Data.List (transpose) +import Data.List.Extra (enumerate) import Data.Map.Lazy qualified as Map import Data.Maybe (isJust) import Data.Set qualified as S @@ -41,7 +42,7 @@ import Swarm.Language.Syntax (Const (..)) import Swarm.Language.Syntax qualified as Syntax import Swarm.Language.Text.Markdown as Markdown (docToMark) import Swarm.Language.Typecheck (inferConst) -import Swarm.Util (listEnums, showT) +import Swarm.Util (showT) -- * Types @@ -189,7 +190,7 @@ capabilityTable a em cs = T.unlines $ header <> map (listToRow mw) capabilityRow header = [listToRow mw capabilityHeader, separatingLine mw] capabilityPage :: PageAddress -> EntityMap -> Text -capabilityPage a em = capabilityTable a em listEnums +capabilityPage a em = capabilityTable a em enumerate -- ** Entities diff --git a/src/swarm-engine/Swarm/Game/Step/Util/Inspect.hs b/src/swarm-engine/Swarm/Game/Step/Util/Inspect.hs index fb8269e8d..df0a3fe75 100644 --- a/src/swarm-engine/Swarm/Game/Step/Util/Inspect.hs +++ b/src/swarm-engine/Swarm/Game/Step/Util/Inspect.hs @@ -9,6 +9,7 @@ import Control.Effect.Lens import Control.Lens hiding (from, use, (%=), (<.>)) import Data.IntMap qualified as IM import Data.List (find) +import Data.List.Extra (enumerate) import Data.Text (Text) import Swarm.Game.Location import Swarm.Game.Robot @@ -16,12 +17,11 @@ import Swarm.Game.State import Swarm.Game.State.Robot import Swarm.Game.Universe import Swarm.Language.Syntax.Direction -import Swarm.Util (listEnums) -- * World queries getNeighborLocs :: Cosmic Location -> [Cosmic Location] -getNeighborLocs loc = map (offsetBy loc . flip applyTurn north . DRelative . DPlanar) listEnums +getNeighborLocs loc = map (offsetBy loc . flip applyTurn north . DRelative . DPlanar) enumerate -- | Get the robot with a given ID. robotWithID :: (Has (State GameState) sig m) => RID -> m (Maybe Robot) diff --git a/src/swarm-lang/Swarm/Language/Parser/Lex.hs b/src/swarm-lang/Swarm/Language/Parser/Lex.hs index 3972f58f3..1568fb878 100644 --- a/src/swarm-lang/Swarm/Language/Parser/Lex.hs +++ b/src/swarm-lang/Swarm/Language/Parser/Lex.hs @@ -49,6 +49,7 @@ import Control.Lens (use, view, (%=), (.=)) import Control.Monad (void) import Data.Char (isLower, isUpper) import Data.Containers.ListUtils (nubOrd) +import Data.List.Extra (enumerate) import Data.Sequence qualified as Seq import Data.Set (Set) import Data.Set qualified as S @@ -58,7 +59,7 @@ import Swarm.Language.Parser.Core import Swarm.Language.Syntax import Swarm.Language.Syntax.Direction import Swarm.Language.Types (baseTyName) -import Swarm.Util (failT, listEnums, squote) +import Swarm.Util (failT, squote) import Text.Megaparsec import Text.Megaparsec.Char import Text.Megaparsec.Char.Lexer qualified as L @@ -156,7 +157,7 @@ operatorChar = T.singleton <$> oneOf opChars -- | Names of base types built into the language. baseTypeNames :: [Text] -baseTypeNames = map baseTyName listEnums +baseTypeNames = map baseTyName enumerate -- | Names of types built into the language. primitiveTypeNames :: [Text] diff --git a/src/swarm-lang/Swarm/Language/Parser/Type.hs b/src/swarm-lang/Swarm/Language/Parser/Type.hs index c4705ffa4..991e42ce5 100644 --- a/src/swarm-lang/Swarm/Language/Parser/Type.hs +++ b/src/swarm-lang/Swarm/Language/Parser/Type.hs @@ -17,6 +17,7 @@ import Control.Monad (join) import Control.Monad.Combinators (many) import Control.Monad.Combinators.Expr (Operator (..), makeExprParser) import Data.Fix (Fix (..), foldFix) +import Data.List.Extra (enumerate) import Data.Maybe (fromMaybe) import Data.Set qualified as S import Swarm.Language.Parser.Core (LanguageVersion (..), Parser, languageVersion) @@ -32,7 +33,6 @@ import Swarm.Language.Parser.Lex ( ) import Swarm.Language.Parser.Record (parseRecord) import Swarm.Language.Types -import Swarm.Util (listEnums) import Text.Megaparsec (choice, optional, some, (<|>)) import Witch (from) @@ -103,7 +103,7 @@ parseTyCon = do SwarmLang0_5 -> reserved -- The latest version requires them to be uppercase SwarmLangLatest -> reservedCS - choice (map (\b -> TCBase b <$ reservedCase (baseTyName b)) listEnums) + choice (map (\b -> TCBase b <$ reservedCase (baseTyName b)) enumerate) <|> TCCmd <$ reservedCase "Cmd" <|> TCUser <$> tyName diff --git a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs index c7d8866cf..8e714771f 100644 --- a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs +++ b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs @@ -30,14 +30,14 @@ module Swarm.Language.Syntax.Constants ( import Data.Aeson.Types hiding (Key) import Data.Data (Data) import Data.Int (Int32) +import Data.List.Extra (enumerate) import Data.Set (Set) import Data.Set qualified as Set import Data.Text hiding (filter, length, map) import Data.Text qualified as T import GHC.Generics (Generic) import Swarm.Language.Syntax.CommandMetadata -import Swarm.Util qualified as Util -import Witch.From (from) +import Swarm.Util (showT) ------------------------------------------------------------ -- Constants @@ -312,7 +312,7 @@ data Const deriving (Eq, Ord, Enum, Bounded, Data, Show, Generic, FromJSON, ToJSON, FromJSONKey, ToJSONKey) allConst :: [Const] -allConst = Util.listEnums +allConst = enumerate data ConstInfo = ConstInfo { syntax :: Text @@ -887,7 +887,7 @@ constInfo c = case c of } lowShow :: Show a => a -> Text - lowShow a = toLower (from (show a)) + lowShow = toLower . showT -- | Maximum perception distance for -- 'Chirp' and 'Sniff' commands diff --git a/src/swarm-scenario/Swarm/Game/Achievement/Definitions.hs b/src/swarm-scenario/Swarm/Game/Achievement/Definitions.hs index 677f6b1a9..b870d0325 100644 --- a/src/swarm-scenario/Swarm/Game/Achievement/Definitions.hs +++ b/src/swarm-scenario/Swarm/Game/Achievement/Definitions.hs @@ -21,11 +21,11 @@ module Swarm.Game.Achievement.Definitions ( ) where import Data.Aeson +import Data.List.Extra (enumerate) import Data.Text (Text) import GHC.Generics (Generic) import Swarm.Language.Syntax (Syntax) import Swarm.Language.Text.Markdown (Document) -import Swarm.Util -- | How hard do we expect the achievement to be? data ExpectedEffort @@ -134,5 +134,5 @@ instance ToJSON GameplayAchievement -- | List of all possible achievements. listAchievements :: [CategorizedAchievement] listAchievements = - map GlobalAchievement listEnums - <> map GameplayAchievement listEnums + map GlobalAchievement enumerate + <> map GameplayAchievement enumerate diff --git a/src/swarm-topography/Swarm/Game/Location.hs b/src/swarm-topography/Swarm/Game/Location.hs index 257ec5356..0df09dc59 100644 --- a/src/swarm-topography/Swarm/Game/Location.hs +++ b/src/swarm-topography/Swarm/Game/Location.hs @@ -45,6 +45,7 @@ import Data.Aeson (FromJSONKey, ToJSONKey) import Data.Function (on, (&)) import Data.Int (Int32) import Data.List (nub) +import Data.List.Extra (enumerate) import Data.Map (Map) import Data.Map qualified as M import Data.Yaml (FromJSON (parseJSON), ToJSON (toJSON)) @@ -146,7 +147,7 @@ applyTurn d = case d of -- Only absolute directions are mapped. cardinalDirs :: M.Map Heading AbsoluteDir cardinalDirs = - M.fromList $ map (toHeading &&& id) Util.listEnums + M.fromList $ map (toHeading &&& id) enumerate -- | Possibly convert a heading into a 'Direction'---that is, if the -- vector happens to be a unit vector in one of the cardinal @@ -174,7 +175,7 @@ relativeTo :: AbsoluteDir -> AbsoluteDir -> PlanarRelativeDir relativeTo targetDir referenceDir = toEnum indexDiff where - enumCount = length (Util.listEnums :: [AbsoluteDir]) + enumCount = length (enumerate :: [AbsoluteDir]) indexDiff = ((-) `on` fromEnum) targetDir referenceDir `mod` enumCount -- | Compute the absolute direction nearest to a given 'Heading'. @@ -189,7 +190,7 @@ nearestDirection coord = index :: Int index = round $ fromIntegral (length orderedDirs) * angle - orderedDirs = Util.listEnumsNonempty + orderedDirs = Util.enumerateNonEmpty -- | Convert a 'Direction' into a corresponding 'Heading'. Note that -- this only does something reasonable for 'DNorth', 'DSouth', 'DEast', diff --git a/src/swarm-tui/Swarm/TUI/Controller.hs b/src/swarm-tui/Swarm/TUI/Controller.hs index 1d99ab134..2115bf642 100644 --- a/src/swarm-tui/Swarm/TUI/Controller.hs +++ b/src/swarm-tui/Swarm/TUI/Controller.hs @@ -56,6 +56,7 @@ import Control.Monad.State (MonadState, execState) import Data.Bits import Data.Foldable (toList) import Data.Int (Int32) +import Data.List.Extra (enumerate) import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE import Data.Map qualified as M @@ -970,7 +971,7 @@ doGoalUpdates = do focusRing $ map GoalWidgets $ if hasMultiple - then listEnums + then enumerate else [GoalSummary] -- The "uiGoal" field is necessary at least to "persist" the data that is needed diff --git a/src/swarm-tui/Swarm/TUI/Editor/Model.hs b/src/swarm-tui/Swarm/TUI/Editor/Model.hs index 8d294b88a..ede281fbd 100644 --- a/src/swarm-tui/Swarm/TUI/Editor/Model.hs +++ b/src/swarm-tui/Swarm/TUI/Editor/Model.hs @@ -8,6 +8,7 @@ module Swarm.TUI.Editor.Model where import Brick.Focus import Brick.Widgets.List qualified as BL import Control.Lens hiding (from, (.=), (<.>)) +import Data.List.Extra (enumerate) import Data.Map qualified as M import Data.Vector qualified as V import Swarm.Game.Display (Display) @@ -18,7 +19,6 @@ import Swarm.Game.Terrain (TerrainType) import Swarm.Game.Universe import Swarm.Game.World.Coords import Swarm.TUI.Model.Name -import Swarm.Util import System.Clock data BoundsSelectionStep @@ -82,7 +82,7 @@ initialWorldEditor ts = (BL.list TerrainList (V.fromList []) 1) (BL.list EntityPaintList (V.fromList []) 1) bounds - (focusRing $ map WorldEditorPanelControl listEnums) + (focusRing $ map WorldEditorPanelControl enumerate) "mymap.yaml" Nothing where diff --git a/src/swarm-tui/Swarm/TUI/Launch/Controller.hs b/src/swarm-tui/Swarm/TUI/Launch/Controller.hs index a37566037..23e360b05 100644 --- a/src/swarm-tui/Swarm/TUI/Launch/Controller.hs +++ b/src/swarm-tui/Swarm/TUI/Launch/Controller.hs @@ -12,6 +12,7 @@ import Brick.Widgets.FileBrowser qualified as FB import Control.Lens import Control.Monad (forM_, when) import Control.Monad.IO.Class (liftIO) +import Data.List.Extra (enumerate) import Data.Maybe (listToMaybe) import Graphics.Vty qualified as V import Swarm.Game.Scenario.Status (ParameterizableLaunchParams (LaunchParams)) @@ -23,7 +24,6 @@ import Swarm.TUI.Model import Swarm.TUI.Model.Name import Swarm.TUI.Model.StateUpdate import Swarm.TUI.Model.UI -import Swarm.Util (listEnums) updateFocusRing :: EditingLaunchParams -> EventM Name LaunchOptions () updateFocusRing parsedParams = do @@ -35,7 +35,7 @@ updateFocusRing parsedParams = do maybeCurrentFocus = focusGetCurrent currentRing refocusRing = maybe id focusSetCurrent maybeCurrentFocus - controls . scenarioConfigFocusRing .= refocusRing (makeFocusRingWith $ modifyRingMembers listEnums) + controls . scenarioConfigFocusRing .= refocusRing (makeFocusRingWith $ modifyRingMembers enumerate) cacheValidatedInputs :: EventM Name LaunchOptions () cacheValidatedInputs = do diff --git a/src/swarm-tui/Swarm/TUI/Launch/Prep.hs b/src/swarm-tui/Swarm/TUI/Launch/Prep.hs index 2a781d681..11f8fcca9 100644 --- a/src/swarm-tui/Swarm/TUI/Launch/Prep.hs +++ b/src/swarm-tui/Swarm/TUI/Launch/Prep.hs @@ -17,6 +17,7 @@ import Control.Carrier.Throw.Either (runThrow) import Control.Lens ((.=), (^.)) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Functor.Identity (runIdentity) +import Data.List.Extra (enumerate) import Data.Text qualified as T import Swarm.Game.Failure (SystemFailure) import Swarm.Game.Scenario.Status (ParameterizableLaunchParams (..), ScenarioInfoPair, getLaunchParams, scenarioStatus) @@ -25,7 +26,6 @@ import Swarm.Game.World.Gen (Seed) import Swarm.Language.Pretty (prettyText) import Swarm.TUI.Launch.Model import Swarm.TUI.Model.Name -import Swarm.Util (listEnums) import Swarm.Util.Effect (withThrow) import System.FilePath (takeDirectory) import Text.Read (readEither) @@ -87,7 +87,7 @@ initConfigPanel = do (LaunchParams (Right Nothing) (Right Nothing)) where myForm = initEditorWidget "" - ring = makeFocusRingWith listEnums + ring = makeFocusRingWith enumerate initFileBrowserWidget :: (MonadIO m) => diff --git a/src/swarm-tui/Swarm/TUI/Model/Goal.hs b/src/swarm-tui/Swarm/TUI/Model/Goal.hs index 5c1e4688f..d7c0413b4 100644 --- a/src/swarm-tui/Swarm/TUI/Model/Goal.hs +++ b/src/swarm-tui/Swarm/TUI/Model/Goal.hs @@ -11,6 +11,7 @@ import Brick.Focus import Brick.Widgets.List qualified as BL import Control.Lens (makeLenses, view, (^..)) import Data.Aeson +import Data.List.Extra (enumerate) import Data.List.NonEmpty (NonEmpty, nonEmpty) import Data.List.NonEmpty qualified as NE import Data.Map (Map) @@ -22,7 +23,6 @@ import Servant.Docs qualified as SD import Swarm.Game.Scenario.Objective import Swarm.Game.Scenario.Objective.WinCheck import Swarm.TUI.Model.Name -import Swarm.Util (listEnums) -- | These are intended to be used as keys in a map -- of lists of goals. @@ -90,7 +90,7 @@ emptyGoalDisplay = GoalDisplay (GoalTracking mempty mempty) (BL.list (GoalWidgets ObjectivesList) mempty 1) - (focusRing $ map GoalWidgets listEnums) + (focusRing $ map GoalWidgets enumerate) hasAnythingToShow :: GoalTracking -> Bool hasAnythingToShow (GoalTracking ann g) = not (null ann && null g) diff --git a/src/swarm-tui/Swarm/TUI/Model/Menu.hs b/src/swarm-tui/Swarm/TUI/Model/Menu.hs index 8c7e40ec3..83c5356e1 100644 --- a/src/swarm-tui/Swarm/TUI/Model/Menu.hs +++ b/src/swarm-tui/Swarm/TUI/Model/Menu.hs @@ -14,6 +14,7 @@ module Swarm.TUI.Model.Menu where import Brick.Widgets.Dialog (Dialog) import Brick.Widgets.List qualified as BL import Control.Lens hiding (from, (<.>)) +import Data.List.Extra (enumerate) import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE import Data.Map qualified as M @@ -32,7 +33,6 @@ import Swarm.Game.ScenarioInfo ( ) import Swarm.Game.World.Gen (Seed) import Swarm.TUI.Model.Name -import Swarm.Util import System.FilePath (dropTrailingPathSeparator, splitPath, takeFileName) import Witch (into) @@ -96,7 +96,7 @@ data Menu | AboutMenu mainMenu :: MainMenuEntry -> BL.List Name MainMenuEntry -mainMenu e = BL.list MenuList (V.fromList listEnums) 1 & BL.listMoveToElement e +mainMenu e = BL.list MenuList (V.fromList enumerate) 1 & BL.listMoveToElement e makePrisms ''Menu diff --git a/src/swarm-tui/Swarm/TUI/Model/StateUpdate.hs b/src/swarm-tui/Swarm/TUI/Model/StateUpdate.hs index a22033d23..16811ea3c 100644 --- a/src/swarm-tui/Swarm/TUI/Model/StateUpdate.hs +++ b/src/swarm-tui/Swarm/TUI/Model/StateUpdate.hs @@ -35,6 +35,7 @@ import Control.Monad.State (MonadState, execStateT) import Data.Bifunctor (first) import Data.Foldable qualified as F import Data.List qualified as List +import Data.List.Extra (enumerate) import Data.List.NonEmpty qualified as NE import Data.Map qualified as M import Data.Maybe (fromMaybe, isJust) @@ -88,7 +89,6 @@ import Swarm.TUI.Model.UI import Swarm.TUI.View.Attribute.Attr (getWorldAttrName, swarmAttrMap) import Swarm.TUI.View.Attribute.CustomStyling (toAttrPair) import Swarm.TUI.View.Structure qualified as SR -import Swarm.Util (listEnums) import Swarm.Util.Effect (asExceptT, withThrow) import System.Clock @@ -291,7 +291,7 @@ scenarioToUIState isAutoplaying siPair@(scenario, _) gs u = do & uiGameplay . uiStructure .~ StructureDisplay (SR.makeListWidget . M.elems $ gs ^. discovery . structureRecognition . automatons . originalStructureDefinitions) - (focusSetCurrent (StructureWidgets StructuresList) $ focusRing $ map StructureWidgets listEnums) + (focusSetCurrent (StructureWidgets StructuresList) $ focusRing $ map StructureWidgets enumerate) where entityList = EU.getEntitiesForList $ gs ^. landscape . terrainAndEntities . entityMap diff --git a/src/swarm-tui/Swarm/TUI/Model/Structure.hs b/src/swarm-tui/Swarm/TUI/Model/Structure.hs index 14d12fd91..af0ed5ec8 100644 --- a/src/swarm-tui/Swarm/TUI/Model/Structure.hs +++ b/src/swarm-tui/Swarm/TUI/Model/Structure.hs @@ -10,11 +10,11 @@ module Swarm.TUI.Model.Structure where import Brick.Focus import Brick.Widgets.List qualified as BL import Control.Lens (makeLenses) +import Data.List.Extra (enumerate) import Swarm.Game.Entity (Entity) import Swarm.Game.Scenario (Cell) import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.TUI.Model.Name -import Swarm.Util (listEnums) data StructureDisplay = StructureDisplay { _structurePanelListWidget :: BL.List Name (StructureInfo Cell Entity) @@ -29,4 +29,4 @@ emptyStructureDisplay :: StructureDisplay emptyStructureDisplay = StructureDisplay (BL.list (StructureWidgets StructuresList) mempty 1) - (focusRing $ map StructureWidgets listEnums) + (focusRing $ map StructureWidgets enumerate) diff --git a/src/swarm-tui/Swarm/TUI/Model/UI.hs b/src/swarm-tui/Swarm/TUI/Model/UI.hs index 4799ba216..dc0ecbf1c 100644 --- a/src/swarm-tui/Swarm/TUI/Model/UI.hs +++ b/src/swarm-tui/Swarm/TUI/Model/UI.hs @@ -65,6 +65,7 @@ import Control.Effect.Accum import Control.Effect.Lift import Control.Lens hiding (from, (<.>)) import Data.Bits (FiniteBits (finiteBitSize)) +import Data.List.Extra (enumerate) import Data.Map (Map) import Data.Map qualified as M import Data.Sequence (Seq) @@ -319,7 +320,7 @@ uiGameplay :: Lens' UIState UIGameplay -- focus ring. However, the REPL already uses Tab. So, to is not used -- at all right now for navigating the toplevel focus ring. initFocusRing :: FocusRing Name -initFocusRing = focusRing $ map FocusablePanel listEnums +initFocusRing = focusRing $ map FocusablePanel enumerate -- | The initial tick speed. defaultInitLgTicksPerSecond :: Int diff --git a/src/swarm-tui/Swarm/TUI/View.hs b/src/swarm-tui/Swarm/TUI/View.hs index 995c5c409..2220a1503 100644 --- a/src/swarm-tui/Swarm/TUI/View.hs +++ b/src/swarm-tui/Swarm/TUI/View.hs @@ -57,6 +57,7 @@ import Data.Functor (($>)) import Data.IntMap qualified as IM import Data.List (intersperse) import Data.List qualified as L +import Data.List.Extra (enumerate) import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE import Data.List.Split (chunksOf) @@ -379,7 +380,7 @@ makeBestScoreRows scenarioStat = , Just $ describeProgress b ) where - maxLeftColumnWidth = maximum (map (T.length . describeCriteria) listEnums) + maxLeftColumnWidth = maximum (map (T.length . describeCriteria) enumerate) mkCriteriaRow = withAttr dimAttr . padLeft Max diff --git a/src/swarm-util/Swarm/Language/Syntax/Direction.hs b/src/swarm-util/Swarm/Language/Syntax/Direction.hs index c350b1f2b..5c8199b17 100644 --- a/src/swarm-util/Swarm/Language/Syntax/Direction.hs +++ b/src/swarm-util/Swarm/Language/Syntax/Direction.hs @@ -25,11 +25,11 @@ import Data.Char qualified as C (toLower) import Data.Data (Data) import Data.Hashable (Hashable) import Data.List qualified as L (drop) +import Data.List.Extra (enumerate) import Data.Text hiding (filter, length, map) +import Data.Text qualified as T import GHC.Generics (Generic) -import Swarm.Util qualified as Util import Swarm.Util.JSON (optionsMinimize) -import Witch.From (from) ------------------------------------------------------------ -- Directions @@ -126,7 +126,7 @@ instance ToJSON Direction where -- | Direction name is generated from the deepest nested data constructor -- e.g. 'DLeft' becomes "left" directionSyntax :: Direction -> Text -directionSyntax d = from $ directionJsonModifier $ case d of +directionSyntax d = T.pack $ directionJsonModifier $ case d of DAbsolute x -> show x DRelative x -> case x of DPlanar y -> show y @@ -139,4 +139,4 @@ isCardinal = \case _ -> False allDirs :: [Direction] -allDirs = map DAbsolute Util.listEnums <> map DRelative (DDown : map DPlanar Util.listEnums) +allDirs = map DAbsolute enumerate <> map DRelative (DDown : map DPlanar enumerate) diff --git a/src/swarm-util/Swarm/Util.hs b/src/swarm-util/Swarm/Util.hs index ffdb9b100..6f4ae556f 100644 --- a/src/swarm-util/Swarm/Util.hs +++ b/src/swarm-util/Swarm/Util.hs @@ -15,8 +15,7 @@ module Swarm.Util ( maximum0, enumeratedMap, cycleEnum, - listEnums, - listEnumsNonempty, + enumerateNonEmpty, showEnum, indexWrapNonEmpty, uniq, @@ -96,6 +95,7 @@ import Data.IntMap.Strict (IntMap) import Data.IntMap.Strict qualified as IM import Data.List (foldl', maximumBy, partition) import Data.List qualified as List +import Data.List.Extra (enumerate) import Data.List.NonEmpty (NonEmpty ((:|))) import Data.List.NonEmpty qualified as NE import Data.Map (Map) @@ -156,13 +156,10 @@ cycleEnum e | e == maxBound = minBound | otherwise = succ e -listEnums :: (Enum e, Bounded e) => [e] -listEnums = [minBound .. maxBound] - --- | Members of the Bounded class are guaranteed to --- have at least one element. -listEnumsNonempty :: (Enum e, Bounded e) => NonEmpty e -listEnumsNonempty = NE.fromList listEnums +-- | See +-- https://hackage.haskell.org/package/relude-1.2.1.0/docs/Relude-Enum.html#v:universeNonEmpty +enumerateNonEmpty :: (Enum e, Bounded e) => NonEmpty e +enumerateNonEmpty = minBound :| drop 1 enumerate -- | We know by the syntax rules of Haskell that constructor -- names must consist of one or more symbols!