diff options
author | Jasper Van der Jeugt <m@jaspervdj.be> | 2016-07-03 14:17:33 +0200 |
---|---|---|
committer | Jasper Van der Jeugt <m@jaspervdj.be> | 2016-07-03 18:15:57 +0200 |
commit | d56edbd043ac3d6f67d9f8cdf54b2c826f6da2e2 (patch) | |
tree | 01ec7e3c1d5b04148161aa3833c7c0cb31ea6fcd /lib | |
parent | ad677e6a22d22041d83162e714c5551c9a4d62d8 (diff) | |
download | stylish-haskell-d56edbd043ac3d6f67d9f8cdf54b2c826f6da2e2.tar.gz |
Put Align in a separate helper module
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Language/Haskell/Stylish/Align.hs | 96 | ||||
-rw-r--r-- | lib/Language/Haskell/Stylish/Step/Records.hs | 66 |
2 files changed, 99 insertions, 63 deletions
diff --git a/lib/Language/Haskell/Stylish/Align.hs b/lib/Language/Haskell/Stylish/Align.hs new file mode 100644 index 0000000..bfee9d2 --- /dev/null +++ b/lib/Language/Haskell/Stylish/Align.hs @@ -0,0 +1,96 @@ +-------------------------------------------------------------------------------- +-- | This module is useful for aligning things. +module Language.Haskell.Stylish.Align + ( Alignable (..) + , align + ) where + + +-------------------------------------------------------------------------------- +import Data.Char (isSpace) +import Data.List (nub) +import qualified Language.Haskell.Exts.Annotated as H + + +-------------------------------------------------------------------------------- +import Language.Haskell.Stylish.Editor +import Language.Haskell.Stylish.Util + + +-------------------------------------------------------------------------------- +-- | This represent a single line which can be aligned. We have something on +-- the left and the right side, e.g.: +-- +-- > [x] -> x + 1 +-- > ^^^^ ^^^^^ +-- > LEFT RIGHT +-- +-- We also have the container which holds the entire line: +-- +-- > [x] -> x + 1 +-- > ^^^^^^^^^^^^^ +-- > CONTAINER +-- +-- And then we have a "right lead" which is just represented by an 'Int', since +-- @haskell-src-exts@ often does not allow us to access it. In the example this +-- is: +-- +-- > [x] -> x + 1 +-- > ^^^ +-- > RLEAD +-- +-- This info is enough to align a bunch of these lines. Users of this module +-- should construct a list of 'Alignable's representing whatever they want to +-- align, and then call 'align' on that. +data Alignable a = Alignable + { aContainer :: !a + , aLeft :: !a + , aRight :: !a + -- | This is the minimal number of columns we need for the leading part not + -- included in our right string. For example, for datatype alignment, this + -- leading part is the string ":: " so we use 3. + , aRightLead :: !Int + } deriving (Show) + + +-------------------------------------------------------------------------------- +-- | Create changes that perform the alignment. +align + :: Int -- ^ Max columns + -> [Alignable H.SrcSpan] -- ^ Alignables + -> [Change String] -- ^ Changes performing the alignment. +align maxColumns alignment + -- Do not make any change if we would go past the maximum number of columns. + | longestLeft + longestRight > maxColumns = [] + | not (fixable alignment) = [] + | otherwise = map align' alignment + where + -- The longest thing in the left column. + longestLeft = maximum $ map (H.srcSpanEndColumn . aLeft) alignment + + -- The longest thing in the right column. + longestRight = maximum + [ H.srcSpanEndColumn (aRight a) - H.srcSpanStartColumn (aRight a) + + aRightLead a + | a <- alignment + ] + + align' a = changeLine (H.srcSpanStartLine $ aContainer a) $ \str -> + let column = H.srcSpanEndColumn $ aLeft a + (pre, post) = splitAt column str + in [padRight longestLeft (trimRight pre) ++ trimLeft post] + + trimLeft = dropWhile isSpace + trimRight = reverse . trimLeft . reverse + + +-------------------------------------------------------------------------------- +-- | Checks that all the alignables appear on a single line, and that they do +-- not overlap. +fixable :: [Alignable H.SrcSpan] -> Bool +fixable [] = False +fixable fields = all singleLine containers && nonOverlapping containers + where + containers = map aContainer fields + singleLine s = H.srcSpanStartLine s == H.srcSpanEndLine s + nonOverlapping ss = length ss == length (nub $ map H.srcSpanStartLine ss) diff --git a/lib/Language/Haskell/Stylish/Step/Records.hs b/lib/Language/Haskell/Stylish/Step/Records.hs index 1c46024..bbfb895 100644 --- a/lib/Language/Haskell/Stylish/Step/Records.hs +++ b/lib/Language/Haskell/Stylish/Step/Records.hs @@ -5,15 +5,13 @@ module Language.Haskell.Stylish.Step.Records -------------------------------------------------------------------------------- -import Data.Char (isSpace) -import Data.List (nub) import qualified Language.Haskell.Exts.Annotated as H -------------------------------------------------------------------------------- +import Language.Haskell.Stylish.Align import Language.Haskell.Stylish.Editor import Language.Haskell.Stylish.Step -import Language.Haskell.Stylish.Util -------------------------------------------------------------------------------- @@ -27,18 +25,6 @@ records modu = -------------------------------------------------------------------------------- -data Alignable a = Alignable - { aContainer :: !a - , aLeft :: !a - , aRight :: !a - -- | This is the minimal number of columns we need for the leading part not - -- included in our right string. For example, for datatype alignment, this - -- leading part is the string ":: " so we use 3. - , aRightLead :: !Int - } deriving (Show) - - --------------------------------------------------------------------------------- fieldDeclToAlignable :: H.FieldDecl a -> Alignable a fieldDeclToAlignable (H.FieldDecl ann names ty) = Alignable { aContainer = ann @@ -49,53 +35,7 @@ fieldDeclToAlignable (H.FieldDecl ann names ty) = Alignable -------------------------------------------------------------------------------- --- | Align the type of a field -align :: Int -> [Alignable H.SrcSpan] -> [Change String] -align maxColumns alignment - -- Do not make any change if we would go past the maximum number of columns. - | longestLeft + longestRight > maxColumns = info [] - | otherwise = info $ map align' alignment - where - info = - id - -- trace ("Alignable: " ++ show alignment) . - -- trace ("longestLeft: " ++ show longestLeft) . - -- trace ("longestRight: " ++ show longestRight) - - -- The longest thing in the left column. - longestLeft = maximum $ map (H.srcSpanEndColumn . aLeft) alignment - - -- The longest thing in the right column. - longestRight = maximum - [ H.srcSpanEndColumn (aRight a) - H.srcSpanStartColumn (aRight a) - + aRightLead a - | a <- alignment - ] - - align' a = changeLine (H.srcSpanStartLine $ aContainer a) $ \str -> - let column = H.srcSpanEndColumn $ aLeft a - (pre, post) = splitAt column str - in [padRight longestLeft (trimRight pre) ++ trimLeft post] - - trimLeft = dropWhile isSpace - trimRight = reverse . trimLeft . reverse - - --------------------------------------------------------------------------------- --- | Checks that all no field of the record appears on more than one line, --- amonst other things -fixable :: [Alignable H.SrcSpan] -> Bool -fixable [] = False -fixable fields = all singleLine containers && nonOverlapping containers - where - containers = map aContainer fields - singleLine s = H.srcSpanStartLine s == H.srcSpanEndLine s - nonOverlapping ss = length ss == length (nub $ map H.srcSpanStartLine ss) - - --------------------------------------------------------------------------------- step :: Int -> Step step maxColumns = makeStep "Records" $ \ls (module', _) -> - let module'' = fmap H.srcInfoSpan module' - fixableRecords = filter fixable $ records module'' - in applyChanges (fixableRecords >>= align maxColumns) ls + let module'' = fmap H.srcInfoSpan module' in + applyChanges (records module'' >>= align maxColumns) ls |