]> git.wincent.com - docvim.git/commitdiff
Add Section visitor
authorGreg Hurrell <greg@hurrell.net>
Tue, 7 Jun 2016 16:26:51 +0000 (09:26 -0700)
committerGreg Hurrell <greg@hurrell.net>
Tue, 7 Jun 2016 16:30:05 +0000 (09:30 -0700)
It's job is to check which kinds of annotations we have, in order to decide
which sections we need to render.

I am probably doing something clowny with the way I am mashing uniplate and
lenses etc together, but it does seem to work. That last `get`/`put` case is a
smell, but needed it given my use of `cosmosOf`; not sure how to avoid that.
Also, not sure why `mapM` does the right thing here, but it does.

docvim.cabal
lib/Docvim/Visitor/Section.hs [new file with mode: 0644]

index d3fc3d95b8c09b86bb737518d63f32162a3acd55..f7d7d5a0c48dc590941016782f15fec3838c6237 100644 (file)
@@ -104,6 +104,7 @@ library
                  ,  Docvim.Visitor.Option
                  ,  Docvim.Visitor.Options
                  ,  Docvim.Visitor.Plugin
+                 ,  Docvim.Visitor.Section
                  ,  Docvim.Visitor.Symbol
                  ,  Paths_docvim
   hs-source-dirs:   lib
diff --git a/lib/Docvim/Visitor/Section.hs b/lib/Docvim/Visitor/Section.hs
new file mode 100644 (file)
index 0000000..7ca30ef
--- /dev/null
@@ -0,0 +1,74 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Docvim.Visitor.Section (getSectionInfo) where
+
+import Control.Lens
+import Control.Monad.State
+import Data.Data.Lens (uniplate)
+import Docvim.AST
+
+data SectionInfo = SectionInfo { _hasCommand :: Bool
+                               , _hasCommands :: Bool
+                               , _hasFunction :: Bool
+                               , _hasFunctions :: Bool
+                               , _hasMapping :: Bool
+                               , _hasMappings :: Bool
+                               , _hasOption :: Bool
+                               , _hasOptions :: Bool
+                               } deriving (Show)
+
+type Env = State SectionInfo
+
+-- Could also have written record setters by hand, but too lazy to do this:
+--
+--     setHasCommand :: SectionInfo -> SectionInfo
+--     setHasCommand info = info { hasCommand = True }
+--
+-- With lenses, we can auto-generate functions that we call like this:
+--
+--     view hasCommand info             (reading)
+--     info ^. hasCommand               (reading, using operator)
+--     set hasCommand True info         (writing)
+--     info & hasCommand .~ True        (writing, using operators)
+--
+-- Or, given that we are using the State monad here, we'll be using the `.=`
+-- operator to update the state using a lens.
+--
+makeLenses ''SectionInfo
+
+defaultSectionInfo :: SectionInfo
+defaultSectionInfo = SectionInfo { _hasCommand = False
+                                 , _hasCommands = False
+                                 , _hasFunction = False
+                                 , _hasFunctions = False
+                                 , _hasMapping = False
+                                 , _hasMappings = False
+                                 , _hasOption = False
+                                 , _hasOptions = False
+                                 }
+
+-- | Walks the supplied AST detecting whether it contains
+-- `@commands`/`@command`, `@functions`/`@function`, `@mappings`/`@mapping` or
+-- `@options`/`@options` sections.
+--
+-- Will be used as follows:
+--   - DO have @commands? -> do nothing
+--   - DON'T have @commands but DO have @command? -> Synthesize CommandsAnnotation
+--   - DON'T we have either? -> do nothing
+--
+getSectionInfo :: Node -> SectionInfo
+getSectionInfo n = execState (mapM check nodes) defaultSectionInfo
+  where
+    nodes = n ^.. cosmosOf uniplate
+    check (CommandAnnotation {}) = hasCommand .= True
+    check CommandsAnnotation     = hasCommands .= True
+    check (FunctionAnnotation _) = hasFunction .= True
+    check FunctionsAnnotation    = hasFunctions .= True
+    check (MappingAnnotation _)  = hasMapping .= True
+    check MappingsAnnotation     = hasMappings .= True
+    check (OptionAnnotation {})  = hasOption .= True
+    check OptionsAnnotation      = hasOptions .= True
+    check _                      = do
+      state <- get
+      put state