packages feed

fix-whitespace (empty) → 0.0.5

raw patch · 6 files changed

+405/−0 lines, 6 filesdep +basedep +directorydep +extra

Dependencies added: base, directory, extra, filepath, filepattern, text, yaml

Files

+ CHANGELOG.md view
@@ -0,0 +1,8 @@+# fix-whitespace++Version history.++## 0.0.5 released 2021-03-11++- initial release+- tested with GHC 8.0.2 - 9.0.1
+ FixWhitespace.hs view
@@ -0,0 +1,225 @@+-- Liang-Ting Chen 2019-10-13:+-- this program is partially re-written so that the configuration part is+-- controlled by a configuration file `fix-whitespace.yaml" in the base+-- directory instead.++import Control.Monad++import Data.Char as Char+import Data.List.Extra (nubOrd)+import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Text.IO as Text  -- Strict IO.++import System.Directory ( getCurrentDirectory, doesFileExist)+import System.Environment+import System.Exit+import System.FilePath+import System.FilePattern+import System.FilePattern.Directory+import System.IO+import System.Console.GetOpt++import ParseConfig++-- Modes.+data Mode+  = Fix    -- ^ Fix whitespace issues.+  | Check  -- ^ Check if there are any whitespace issues.+    deriving (Show, Eq)++type Verbose = Bool++data Options = Options+  { optVerbose :: Verbose+  -- ^ Display the location of a file being checked or not.+  , optHelp    :: Bool+  -- ^ Display the help information.+  , optMode    :: Mode+  , optConfig  :: FilePath+  -- ^ The location to the configuration file.+  }++defaultOptions = Options+  { optVerbose = False+  , optHelp    = False+  , optMode    = Fix+  , optConfig  = "fix-whitespace.yaml"+  }++options :: [OptDescr (Options -> Options)]+options =+  [ Option ['h']     ["help"]+      (NoArg (\opts -> opts { optHelp = True }))+      "Show this help information."+  , Option ['v']     ["verbose"]+      (NoArg (\opts -> opts { optVerbose = True }))+      "Show files as they are being checked."+  , Option []        ["config"]+      (ReqArg (\loc opts -> opts { optConfig = loc }) "CONFIG")+      "Override the project configuration fix-whitespace.yaml."+  , Option []        ["check"]+      (NoArg (\opts -> opts { optMode = Check }))+      (unlines+        [ "With --check the program does not change any files,"+        , "it just checks if any files would have been changed."+        , "In this case it returns with a non-zero exit code."+        ])+  ]++compilerOpts :: String -> IO (Options, [String])+compilerOpts progName = do+  argv <- getArgs+  case getOpt Permute options argv of+      (o, n, []  ) -> return (foldl (flip id) defaultOptions o, n)+      (_, _, errs) -> ioError (userError (concat errs ++ "\n" ++ shortUsageHeader progName))++shortUsageHeader, usageHeader, usage :: String -> String++shortUsageHeader progName =+  "Usage: " ++ progName ++ " [-h|--help] [-v|--verbose] [--check] [--config CONFIG] [FILES]"++usageHeader progName = unlines+  [ shortUsageHeader progName+  , ""+  , "The program does the following"+  , ""+  , "* Removes trailing whitespace."+  , "* Removes trailing lines containing nothing but whitespace."+  , "* Ensures that the file ends in a newline character."+  , ""+  , "for files specified in [FILES] or"+  , ""+  , "\t" ++ optConfig defaultOptions+  , ""+  , "under the current directory."+  , ""+  , "Background: Agda was reported to fail to compile on Windows"+  , "because a file did not end with a newline character (Agda"+  , "uses -Werror)."+  , ""+  , "Available options:"+  ]++usage progName = usageInfo (usageHeader progName) options++main :: IO ()+main = do+  progName <- getProgName+  (opts, nonOpts) <- compilerOpts progName++  -- check if the user asks for help+  when (optHelp opts) $ putStr (usage progName) >> exitSuccess++  -- check if the configuration file exists+  configExist <- doesFileExist $ optConfig opts+  unless (configExist || not (null nonOpts)) $ do+    hPutStr stderr (unlines+      [ "fix-whitespace.yaml is not found and there are no files specified as arguments."+      , ""+      , shortUsageHeader progName+      ])+    exitFailure++  let mode    = optMode    opts+      verbose = optVerbose opts+      config  = optConfig  opts++  base <- getCurrentDirectory++  files <- if not $ null nonOpts+    then getDirectoryFiles base nonOpts+    else do+      Config incDirs0 excDirs0 incFiles excFiles <- parseConfig config+      let incDirs = map (++ "/**/") incDirs0+      let excDirs = map (++ "/**/") excDirs0++      -- File patterns to always include+      -- when not matching an excluded file pattern+      let incWhitelistPatterns = concatMap (\d -> map (d ++) incFiles) incDirs+      -- File patterns to always exclude+      let excBlacklistPatterns = map ("**/" ++) excFiles++      -- Files to include when not in an excluded directory+      -- and when not matching an excluded file pattern+      let incPatterns = map ("**/" ++) incFiles+      -- Directory and file patterns to exclude+      let excPatterns = (map (++ "*") excDirs)+                     ++ (map ("**/" ++) excFiles)++      when verbose $ do+        putStrLn "Include whitelist:"+        putStrLn (unlines incWhitelistPatterns)++        putStrLn "Exclude blacklist:"+        putStrLn (unlines excBlacklistPatterns)++        putStrLn "Include:"+        putStrLn (unlines incPatterns)++        putStrLn "Exclude:"+        putStrLn (unlines excPatterns)++      files0 <- getDirectoryFilesIgnore base incWhitelistPatterns excBlacklistPatterns+      files1 <- getDirectoryFilesIgnore base incPatterns excPatterns+      return (nubOrd (files0 ++ files1))++  changes <- mapM (fix mode verbose) files++  when (or changes && mode == Check) exitFailure++fix :: Mode -> Verbose -> FilePath -> IO Bool+fix mode verbose f = do++  new <- withFile f ReadMode $ \h -> do+    hSetEncoding h utf8+    s <- Text.hGetContents h+    let s' = transform s+    return $ if s' == s then Nothing else Just s'+  case new of+    Nothing -> do+      when verbose (putStrLn $ "[ Checked ] " ++ f)+      return False+    Just s  -> do+      hPutStrLn stderr $+        "[ Violation " +++        (if mode == Fix then "fixed" else "detected") +++        " ] " ++ f+      when (mode == Fix) $+        withFile f WriteMode $ \h -> do+          hSetEncoding h utf8+          Text.hPutStr h s+      return True++-- | Transforms the contents of a file.++transform :: Text -> Text+transform =+  Text.unlines .+  removeFinalEmptyLinesExceptOne .+  map (removeTrailingWhitespace .  convertTabs) .+  Text.lines+  where+  removeFinalEmptyLinesExceptOne =+    reverse . dropWhile1 Text.null . reverse++  removeTrailingWhitespace =+    Text.dropWhileEnd ((`elem` [Space,Format]) . generalCategory)++  convertTabs =+    Text.pack . reverse . fst . foldl convertOne ([], 0) . Text.unpack++  convertOne (a, p) '\t' = (addSpaces n a, p + n)+                           where+                             n = 8 - p `mod` 8+  convertOne (a, p) c = (c:a, p+1)++  addSpaces 0 x = x+  addSpaces n x = addSpaces (n-1) (' ':x)++-- | 'dropWhile' except keep the first of the dropped elements+dropWhile1 :: (a -> Bool) -> [a] -> [a]+dropWhile1 _ [] = []+dropWhile1 p (x:xs)+  | p x       = x : dropWhile p xs+  | otherwise = x : xs
+ LICENSE view
@@ -0,0 +1,25 @@+Copyright (c) 2005-2021 remains with the authors.++fix-whitespace was originally written by Nils Anders Danielsson as+part of Agda 2 with contributions from Ulf Norell,+Andrés Sicard-Ramírez, Andreas Abel, Philipp Hausmann, Jesper Cockx,+Vlad Semenov, and Liang-Ting Chen.++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ParseConfig.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}++module ParseConfig+  ( Config(..)+  , parseConfig+  ) where++import Data.Maybe (fromMaybe)+import qualified Data.Yaml as Y+import Data.Yaml (FromJSON(..), (.:?))++data RawConfig = RawConfig+  { __included_dirs  :: Maybe [FilePath]+  , __excluded_dirs  :: Maybe [FilePath]+  , __included_files :: Maybe [FilePath]+  , __excluded_files :: Maybe [FilePath]+  } deriving Show++data Config = Config+  { included_dirs  :: [FilePath]+  , excluded_dirs  :: [FilePath]+  , included_files :: [FilePath]+  , excluded_files :: [FilePath]+  } deriving Show++instance FromJSON RawConfig where+  parseJSON (Y.Object v) =+    RawConfig <$>+    v .:? "included-dirs"  <*>+    v .:? "excluded-dirs"  <*>+    v .:? "included-files" <*>+    v .:? "excluded-files"+  parseJSON _ = fail "Expected Object for Config value"++parseRawConfig :: FilePath -> IO (Either Y.ParseException RawConfig)+parseRawConfig = Y.decodeFileEither++parseConfig :: FilePath -> IO Config+parseConfig fp = do+  result <- Y.decodeFileEither fp+  case result of+    Left _    -> return defaultConfig+    Right cfg -> return $ defaults cfg+  where+    defaults (RawConfig _incDirs _excDirs _incFiles _excFiles) =+      let incDirs  = fromMaybe [] _incDirs+          excDirs  = fromMaybe [] _excDirs+          incFiles = fromMaybe [] _incFiles+          excFiles = fromMaybe [] _excFiles+      in Config incDirs excDirs incFiles excFiles++defaultConfig :: Config+defaultConfig = Config [] [] [] []
+ README.md view
@@ -0,0 +1,44 @@+fix-whitespace: Fixes whitespace issues+=======================================++[![Hackage version](https://img.shields.io/hackage/v/fix-whitespace.svg?label=Hackage)](http://hackage.haskell.org/package/fix-whitespace)+[![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/fix-whitespace/badge)](https://matrix.hackage.haskell.org/package/fix-whitespace)+[![fix-whitespace on Stackage Nightly](https://stackage.org/package/fix-whitespace/badge/nightly)](https://stackage.org/nightly/package/fix-whitespace)+[![Stackage LTS version](https://www.stackage.org/package/fix-whitespace/badge/lts?label=Stackage)](https://www.stackage.org/package/fix-whitespace)+[![Build status](https://github.com/agda/fix-whitespace/workflows/Build%20by%20Stack/badge.svg)](https://github.com/agda/fix-whitespace/actions)+++This tool can keep your project and repository clean of trailing+whitespace and missing terminal newline.++Usage: `fix-whitespace [-h|--help] [-v|--verbose] [--check] [--config CONFIG] [FILES]`++The program does the following to files specified in `FILES` or in the+configuration file `fix-whitespace.yaml` under the current directory+(and its subdirectories):++  * Remove trailing whitespace.+  * Remove trailing lines containing nothing but whitespace.+  * Ensure that the file ends in a newline character.++Available options:++*  `-h  --help`++   Show this help information.++*  `-v  --verbose`++   Show files as they are being checked.++*  `--config=CONFIG`++   Override the project configuration `fix-whitespace.yaml`.++*  `--check`++   With `--check` the program does not change any files,+   it just checks if any files would have been changed.+   In this case it returns with a non-zero exit code.++For an example configuration file see [the one of Agda](https://github.com/agda/agda/blob/f9a181685397517b5d14943ca88a1c0acacc2075/fix-whitespace.yaml).
+ fix-whitespace.cabal view
@@ -0,0 +1,50 @@+name:            fix-whitespace+version:         0.0.5+cabal-version:   >= 1.10+build-type:      Simple+description:     Removes trailing whitespace, lines containing only whitespace and ensure that every file ends in a newline character.+license:         OtherLicense+license-file:    LICENSE+author:          fix-whitespace was originally written by Nils Anders Danielsson as part of Agda 2 with contributions from Ulf Norell, Andrés Sicard-Ramírez, Andreas Abel, Philipp Hausmann, Jesper Cockx, Vlad Semenov, and Liang-Ting Chen.+homepage:        https://github.com/agda/fix-whitespace+bug-reports:     https://github.com/agda/fix-whitespace/issues+maintainer:      Liang-Ting Chen <liang.ting.chen.tw@gmail.com>+Category:        Text+Synopsis:        Fixes whitespace issues.+tested-with:     GHC == 8.0.2+                 GHC == 8.2.2+                 GHC == 8.4.4+                 GHC == 8.6.5+                 GHC == 8.8.4+                 GHC == 8.10.3+                 GHC == 8.10.4+                 GHC == 9.0.1++extra-source-files:+  CHANGELOG.md+  README.md++source-repository head+  type: git+  location: https://github.com/agda/fix-whitespace.git++executable fix-whitespace+  hs-source-dirs:   .+  main-is:          FixWhitespace.hs+  other-modules:    ParseConfig+  default-language: Haskell2010++  build-depends:  base         >= 4.9.0.0  &&  < 4.16+                , directory    >= 1.2.6.2  &&  < 1.4+                , extra        >= 1.1      &&  < 2.0+                , filepath     >= 1.4.1.0  &&  < 1.5+                , filepattern  >= 0.1.2    &&  < 0.1.3+                , text         >= 1.2.3.0  &&  < 1.3+                , yaml         >= 0.8.4    &&  < 0.12++  -- ASR (2018-10-16).+  -- text-1.2.3.0 required for supporting GHC 8.4.1, 8.4.2 and+  -- 8.4.3. See Issue #3277.+  -- The other GHC versions can restrict to >= 1.2.3.1.+  if impl(ghc < 8.4.1) || impl(ghc > 8.4.3)+    build-depends: text >= 1.2.3.1