CurryInfo: stylechecker-2.0.0 / Check.AST.TopLevel.BlankLines

classes:

              
documentation:

              
name:
Check.AST.TopLevel.BlankLines
operations:
blankLine checkBlankLines checkBlankLines' getEndLi noBlank
sourcecode:
module Check.AST.TopLevel.BlankLines where

import Text.Pretty

import Curry.SpanInfo
import Curry.Span
import Curry.Position
import Curry.Types
import Curry.Ident
import Control.Monad ( unless )

import Types

-- Applies actual check on Modules.
checkBlankLines :: Module a -> Int-> CSM ()
checkBlankLines (Module sI _ _ _ _ _ decls) _ = checkBlankLines' sI decls

-- Checks if there is at least one blank line between top level declarations.
-- If it's a type signature, there is no need to check. Otherwise the check
-- is applied.
checkBlankLines' :: SpanInfo -> [Decl a] -> CSM ()
checkBlankLines' _ []                   = return ()
checkBlankLines' _ (_:[])               = return ()
checkBlankLines' sI (decl1:decl2:decls) =
  case decl1 of
    (TypeSig _ _ _)     -> checkBlankLines' sI (decl2:decls)
    (InfixDecl _ _ _ _) -> case decl2 of
                             (InfixDecl _ _ _ _) ->  checkBlankLines' sI (decl2:decls)
                             _                   ->  do blankLine decl1 decl2
                                                        checkBlankLines' sI (decl2:decls)
    _                   -> do blankLine decl1 decl2
                              checkBlankLines' sI (decl2:decls)

-- Checks if two different top-level declarations are properly aligned
-- (This check might not work if there are comments in between).
-- 
-- Two top-level declarations are misaligned if ...
-- (a) one top level declaration is surrounded by the other (missing spatial separation),
-- (b) or there is no blank line between the two declarations.
blankLine :: Decl a -> Decl a -> CSM ()
blankLine decl1 decl2 = do
  let sI1@(SpanInfo (Span _ (Position l _)) _) = getSpanInfo decl1
      sI2                                      = getSpanInfo decl2
  unless ((getLi sI2) - l > 1) $ 
          if getEndLi sI2 < getEndLi sI1 
            then report (Message
                          (getSpan sI2)
                          (text "Top level declaration is surrounded by another declaration")
                          (text "Move inner declaration below the surrounding declaration," 
                           <+> 
                           text "keeping at least one blank line" )
                        )
            else report (Message
                          (getSpan sI2)
                          ( colorizeKey "Blank Line" <+> text "missing")
                          ( text "leave a"
                            <+> colorizeKey "blank line"
                            <+> text "between"
                            <+> colorizeKey "top level declarations")
                        )

-- Not used yet, checks for "trailing blanklines".
--
-- Because the curry frontend does not account for 
-- trailing blank lines in module spans,
-- this check seems to be unnecessary and should removed in the future.
noBlank :: SpanInfo -> Decl a -> CSM ()
noBlank sI decl = do
  let sID = getSpanInfo decl
  unless (getEndLi sI == getEndLi sID)
          (report (Message
                    (getSpan sI)
                    ( colorizeKey "blank line(s)"
                      <+> text "at the end of"
                      <+> colorizeKey "module"
                    )
                    (text "delete superfluous blank line(s) at end of module" )
                  )
          )

-- Returns line of end position from Spaninfo.
getEndLi :: SpanInfo -> Int
getEndLi si = case si of 
  (SpanInfo (Span _ (Position l _)) _) -> l
  _ -> error "getEndLi: NoSpanInfo"
types:

              
unsafe:
safe