tabs (empty) → 0.1.0.0
raw patch · 8 files changed
+329/−0 lines, 8 filesdep +basedep +filepathdep +monadlistsetup-changed
Dependencies added: base, filepath, monadlist, mtl, tagged
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- src/Main.hs +12/−0
- src/Tabs.hs +75/−0
- src/Text/Indent.hs +10/−0
- src/Text/Indent/Class.hs +17/−0
- src/Text/Indent/Type/CodeGen.hs +141/−0
- tabs.cabal +42/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Thomas Eding + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Thomas Eding nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple +main = defaultMain
+ src/Main.hs view
@@ -0,0 +1,12 @@+module Main ( + main +) where + + +import qualified Tabs + + +main :: IO () +main = Tabs.main + +
+ src/Tabs.hs view
@@ -0,0 +1,75 @@+module Tabs ( + main +) where + + +import Data.Char (isSpace, toLower) +import Data.List (partition) +import Data.Tagged +import System.Environment (getArgs) +import System.FilePath (takeExtension) +import System.IO +import Text.Indent + + +main :: IO () +main = do + args <- getArgs + let (opts, files) = partition ((== "-") . take 1) args + if any (`elem` ["-h", "--help"]) opts + then help + else if null files + then byStdin runIndent + else mapM_ (byFile runIndent) files + + +help :: IO () +help = mapM_ putStrLn [ + "Usage: myindent [options] files" + , "Description: Indents files based on filetype." + ] + + +data IndentType + = HcCodeGen + + +byStdin :: (IndentType -> String -> String) -> IO () +byStdin f = do + contents <- getContents + putStrLn $ f HcCodeGen contents + + +byFile :: (IndentType -> String -> String) -> FilePath -> IO () +byFile f file = do + contents <- readFile file + mIndentType <- case map toLower $ drop 1 $ takeExtension file of + "" -> return Nothing + "c" -> return $ Just HcCodeGen + "cpp" -> return $ Just HcCodeGen + _ -> do + hPutStrLn stderr $ "Uknown filetype: Skipping " ++ file + return Nothing + case mIndentType of + Nothing -> return () + Just indentType -> do + let newContents = f indentType contents + length contents `seq` writeFile file newContents + + +type Indent a = String -> Tagged a String + + +runIndent :: IndentType -> String -> String +runIndent indentType = case indentType of + HcCodeGen -> untag . (indent DropOldTabs :: Indent CodeGen) + + + + + + + + + +
+ src/Text/Indent.hs view
@@ -0,0 +1,10 @@+module Text.Indent ( + module Text.Indent.Class + , module Text.Indent.Type.CodeGen + ) where + + +import Text.Indent.Class +import Text.Indent.Type.CodeGen + +
+ src/Text/Indent/Class.hs view
@@ -0,0 +1,17 @@+module Text.Indent.Class ( + Indenter(..) + , IndentMode(..) + , Tagged + ) where + + +import Data.Tagged + + +data IndentMode = DropOldTabs | KeepOldTabs + + +class Indenter a where + indent :: IndentMode -> String -> Tagged a String + +
+ src/Text/Indent/Type/CodeGen.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE EmptyDataDecls #-} +{-# LANGUAGE ViewPatterns #-} + +module Text.Indent.Type.CodeGen ( + CodeGen +) where + + +import Control.Arrow +import Control.Monad.ListM +import Control.Monad.State.Strict +import Data.Char +import Data.List +import Data.Maybe +import Data.Tagged +import Text.Indent.Class + + +data CodeGen + + +type IndentAmount = Int +type Line = String + + +data IndentItem + = Open + | Begin + | Brace + deriving (Show, Eq) + + +data MalformedStack = MalformedStack + + +data IndentState = IndentState { + indentAmount :: IndentAmount, + itemStack :: Either MalformedStack [IndentItem] +} + + +initState :: IndentState +initState = IndentState { + indentAmount = 0, + itemStack = Right [] } + + +instance Indenter CodeGen where + indent mode = Tagged . unlines . flip evalState initState . mapM (tabify . wsOp) . lines + where + wsOp = case mode of + DropOldTabs -> dropWs + KeepOldTabs -> id + tabify line = do + newTabAmount <- calculateTabs $ dropWs line + return $ replicate newTabAmount '\t' ++ line + + +dropWs :: String -> String +dropWs = dropWhile isSpace + + +lastNonWs :: String -> Maybe Char +lastNonWs s = case dropWs $ reverse s of + [] -> Nothing + c : _ -> Just c + + +calculateTabs :: Line -> State IndentState IndentAmount +calculateTabs line = case line of + (stripPrefix "DEFINE" -> Just (stripPrefix "(" . dropWs -> Just rest)) -> calculateTabs rest + (isPrefixOf "HC_Open_" -> True) -> do + n <- gets indentAmount + push Open + return n + (isPrefixOf "HC_KOpen_" -> True) -> do + n <- gets indentAmount + push Open + return n + (isPrefixOf "HC_Close_" -> True) -> do + pop Open + gets indentAmount + (isPrefixOf "HC_Begin_" -> True) -> do + n <- gets indentAmount + push Begin + return n + (isPrefixOf "HC_End_" -> True) -> do + pop Begin + gets indentAmount + (lastNonWs -> Just '{') -> do + n <- gets indentAmount + push Brace + return n + (isPrefixOf "}" -> True) -> do + popTill Brace + pop Brace + gets indentAmount + _ -> gets indentAmount + + +topItem :: State IndentState (Maybe IndentItem) +topItem = gets $ either (const Nothing) listToMaybe . itemStack + + +push :: IndentItem -> State IndentState () +push item = do + modify $ \st -> st { itemStack = fmap (item :) $ itemStack st } + modify $ \st -> st { indentAmount = indentAmount st + 1 } + + +pop :: IndentItem -> State IndentState () +pop item = do + mItem <- topItem + if Just item == mItem + then do + modify $ \st -> st { itemStack = fmap tail $ itemStack st } + modify $ \st -> st { indentAmount = indentAmount st - 1 } + else modify $ \st -> st { itemStack = Left MalformedStack } + + +popTill :: IndentItem -> State IndentState () +popTill destItem = do + mItem <- topItem + case mItem of + Nothing -> return () + Just item -> if item == destItem + then return () + else do + pop item + popTill destItem + + + + + + + + + + +
+ tabs.cabal view
@@ -0,0 +1,42 @@+-- Initial indent.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +name: tabs +version: 0.1.0.0 +synopsis: Indents source files +-- description: +license: BSD3 +license-file: LICENSE +author: Thomas Eding +maintainer: thomasedingcode@gmail.com +-- copyright: +-- category: +build-type: Simple +cabal-version: >=1.8 + + +library + build-depends: + base >=4.10 && <4.11, tagged >=0.8 && <0.9, filepath >=1.4 && <1.5, monadlist >=0.0 && <0.1, mtl >=2.2 && <2.3 + hs-source-dirs: + src + exposed-modules: + Text.Indent + Text.Indent.Class + Text.Indent.Type.CodeGen + +executable tabs + main-is: + Main.hs + build-depends: + base >=4.10 && <4.11, tagged >=0.8 && <0.9, filepath >=1.4 && <1.5, monadlist >=0.0 && <0.1, mtl >=2.2 && <2.3 + hs-source-dirs: + src + other-modules: + Tabs + Text.Indent + Text.Indent.Class + Text.Indent.Type.CodeGen + + +