Giter VIP home page Giter VIP logo

stylish-haskell's Introduction

stylish-haskell

Stack Build Status Cabal Build Status

Introduction

A simple Haskell code prettifier. The goal is not to format all of the code in a file, since I find those kind of tools often "get in the way". However, manually cleaning up import statements etc. gets tedious very quickly.

This tool tries to help where necessary without getting in the way.

Installation

You can install it using stack install stylish-haskell or cabal install stylish-haskell.

You can also install it using your package manager:

  • Debian 9 or later: apt-get install stylish-haskell
  • Ubuntu 16.10 or later: apt-get install stylish-haskell
  • Arch Linux: pacman -S stylish-haskell

Features

  • Aligns and sorts import statements
  • Groups and wraps {-# LANGUAGE #-} pragmas, can remove (some) redundant pragmas
  • Removes trailing whitespace
  • Aligns branches in case and fields in records
  • Converts line endings (customizable)
  • Replaces tabs by four spaces (turned off by default)
  • Replaces some ASCII sequences by their Unicode equivalents (turned off by default)
  • Format data constructors and fields in records.

Feature requests are welcome! Use the issue tracker for that.

Example

Turns:

{-# LANGUAGE ViewPatterns, TemplateHaskell #-}
{-# LANGUAGE GeneralizedNewtypeDeriving,
            ViewPatterns,
    ScopedTypeVariables #-}

module Bad where

import Control.Applicative ((<$>))
import System.Directory (doesFileExist)

import qualified Data.Map as M
import      Data.Map    ((!), keys, Map)

data Point = Point { pointX, pointY :: Double , pointName :: String} deriving (Show)

into:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE TemplateHaskell            #-}

module Bad where

import           Control.Applicative ((<$>))
import           System.Directory    (doesFileExist)

import           Data.Map            (Map, keys, (!))
import qualified Data.Map            as M

data Point = Point
    { pointX, pointY :: Double
    , pointName      :: String
    } deriving (Show)

Configuration

The tool is customizable to some extent. It tries to find a config file in the following order:

  1. A file passed to the tool using the -c/--config argument
  2. .stylish-haskell.yaml in the current directory (useful for per-directory settings)
  3. .stylish-haskell.yaml in the nearest ancestor directory (useful for per-project settings)
  4. stylish-haskell/config.yaml in the platform’s configuration directory (on Windows, it is %APPDATA%, elsewhere it defaults to ~/.config and can be overridden by the XDG_CONFIG_HOME environment variable; useful for user-wide settings)
  5. .stylish-haskell.yaml in your home directory (useful for user-wide settings)
  6. The default settings.

Use stylish-haskell --defaults > .stylish-haskell.yaml to dump a well-documented default configuration to a file, this way you can get started quickly.

Record formatting

Basically, stylish-haskell supports 4 different styles of records, controlled by records in the config file.

Here's an example of all four styles:

-- equals: "indent 2", "first_field": "indent 2"
data Foo a
  = Foo
      { a :: Int
      , a2 :: String
        -- ^ some haddock
      }
  | Bar
      { b :: a
      }
  deriving (Eq, Show)
  deriving (ToJSON) via Bar Foo

-- equals: "same_line", "first_field": "indent 2"
data Foo a = Foo
               { a :: Int
               , a2 :: String
                 -- ^ some haddock
               }
           | Bar
               { b :: a
               }
  deriving (Eq, Show)
  deriving (ToJSON) via Bar Foo

-- equals: "same_line", "first_field": "same_line"
data Foo a = Foo { a :: Int
                 , a2 :: String
                   -- ^ some haddock
                 }
           | Bar { b :: a
                 }
  deriving (Eq, Show)
  deriving (ToJSON) via Bar Foo

-- equals: "indent 2", first_field: "same_line"
data Foo a
  = Foo { a :: Int
        , a2 :: String
          -- ^ some haddock
        }
  | Bar { b :: a
        }
  deriving (Eq, Show)
  deriving (ToJSON) via Bar Foo

Editor integration

Haskell Language Server

Haskell Language Server(HLS) includes a plugin for stylish-haskell. By changing the formatting provider option (haskell.formattingProvider) to stylish-haskell as described in HLS options, any editors that support Language Server Protocol can use stylish-haskell for formatting.

VIM integration

Since it works as a filter it is pretty easy to integrate this with VIM.

You can call

:%!stylish-haskell

and add a keybinding for it.

Or you can define formatprg

:set formatprg=stylish-haskell

and then use gq.

Alternatively, [vim-autoformat] supports stylish-haskell. To have it automatically reformat the files on save, add to your vimrc:

autocmd BufWrite *.hs :Autoformat
" Don't automatically indent on save, since vim's autoindent for haskell is buggy
autocmd FileType haskell let b:autoformat_autoindent=0

There are also plugins that run stylish-haskell automatically when you save a Haskell file:

Emacs integration

haskell-mode for Emacs supports stylish-haskell. For configuration, see the “Using external formatters” section of the haskell-mode manual.

Atom integration

ide-haskell for Atom supports stylish-haskell.

atom-beautify for Atom supports Haskell using stylish-haskell.

Visual Studio Code integration

stylish-haskell-vscode for VSCode supports stylish-haskell.

Using with Continuous Integration

You can quickly grab the latest binary and run stylish-haskell like so:

curl -sL https://raw.github.com/haskell/stylish-haskell/master/scripts/latest.sh | sh -s .

Where the . can be replaced with the arguments you pass to stylish-haskell.

Credits

Written and maintained by Jasper Van der Jeugt.

Contributors:

  • Chris Done
  • Hiromi Ishii
  • Leonid Onokhov
  • Michael Snoyman
  • Mikhail Glushenkov
  • Beatrice Vergani
  • Paweł Szulc
  • Łukasz Gołębiewski
  • Felix Mulder

stylish-haskell's People

Contributors

1computer1 avatar 23skidoo avatar akrmn avatar andreasabel avatar bergmark avatar cblp avatar chris-martin avatar danburton avatar encodepanda avatar felixonmars avatar fosskers avatar jaspervdj avatar july541 avatar konn avatar langston-barrett avatar lev135 avatar lukasz-golebiewski avatar maksbotan avatar michaelpj avatar mmzx avatar nbouscal avatar nicuveo avatar nightuser avatar peterbecich avatar phadej avatar sopvop avatar spwhitton avatar themoritz avatar tikhonjelvis avatar zlondrej avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stylish-haskell's Issues

UnicodeSyntax

GHC has an extension called UnicodeSyntax that lets you use unicode symbols for various keywords in the Haskell syntax. It would be nice if I could automatically UnicodeSyntaxify code. This would involve adding the extension at the top of the module if not already there, and replacing all replaceable symbols with their unicode counterparts.

Doesn't support hash-bangs

If I have a Haskell script that starts with "#!/usr/bin/runhaskell" and stylish-haskell is run (automatically by my editor, so I'm not choosing to do this for a simple script ;-) then I get this error:

Warning (stylish-haskell): stylish-haskell failed:
    Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>:
    ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 1, srcColumn
    = 1}) "Parse error: #!/"

Asks for MultiParamTypeClasses when only FlexibleContexts is Needed

Running on the following file complains that MultiParamTypeClasses is not enabled, but only FlexibleContexts is needed for it to compile.

{-# LANGUAGE FlexibleContexts #-}

import Control.Monad.Reader

main = undefined

foo :: (MonadReader String m) => m ()
foo = undefined

gives the error

Language.Haskell.Stylish.Parse.parseModule: could not parse test.hs: ParseFailed (Src
Loc {srcFilename = "<unknown>.hs", srcLine = 7, srcColumn = 34}) "MultiParamTypeClass
es is not enabled"                   

For now I'm just putting the extra pragma but it would be nice if I didn't have to.

bug in parsing?

for file

{-#LANGUAGE DataKinds , TypeFamilies, ConstraintKinds, TypeOperators, PolyKinds #-}

module where

i=1


i get this error

carter code/Cashew ‹master*› » stylish-haskell ReadMe.md hatrix/Math/Indexors/Dense.hs
Language.Haskell.Stylish.Parse.parseModule: could not parse hatrix/Math/Indexors/Dense.hs: ParseFailed (SrcLoc {srcFilename = ".hs", srcLine = 3, srcColumn = 8}) "Parse error: where"
{-#LANGUAGE DataKinds , TypeFamilies, ConstraintKinds, TypeOperators, PolyKinds #-}

module where

i=1 %

DataKinds parse error

Hello! I'm getting parse error on following Haskell code:

[...]
(~^) = defFunction2 (\a b -> get1 (member (Proxy :: Proxy "pow") (get0 a)) b)
[...]

Please note, that :: Proxy "pow" is a valid Haskell expression if the Proxy was defined in the following way:

data Proxy a = Proxy

with -XDataKinds extension enabled.

The error:

Language.Haskell.Stylish.Parse.parseModule: could not parse Std.hs: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 23, srcColumn = 59}) "Parse error: \"pow\"

Weird record alignment behaviour

Input:

import qualified Language.Haskell.Stylish.Step.TrailingWhitespace as TrailingWhitespace
import           Paths_stylish_haskell                           
                                                                   (getDataFileName)

data Config = Config
    { configSteps              :: [Step]
    , configColumns            :: Int
    , configLanguageExtensions :: [String]
    }

Config:

steps:
  - imports: {}
  - records: {}

Output:

import qualified Language.Haskell.Stylish.Step.TrailingWhitespace as TrailingWhitespace
import           Paths_stylish_haskell                           
                                                                   (getDataFileName)

data Config = Config
    { configSteps              :: [Step]
    , configColumn             s            :: Int
    , configLanguage           Extensions :: [String]
    }                          

Too tired to debug it now.

LANGUAGE pragma sorting does not respect #if defined / #endif blocks

As example, saving a file with the following pragma list removes PolyKinds form within the #if block and adds it to the list above:

{-# LANGUAGE BangPatterns              #-}
{-# LANGUAGE CPP                       #-}
{-# LANGUAGE DeriveGeneric             #-}
{-# LANGUAGE FlexibleContexts          #-}
{-# LANGUAGE FlexibleInstances         #-}
{-# LANGUAGE FunctionalDependencies    #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE RankNTypes                #-}
{-# LANGUAGE RecordWildCards           #-}
{-# LANGUAGE ScopedTypeVariables       #-}
{-# LANGUAGE TemplateHaskell           #-}
{-# LANGUAGE TypeFamilies              #-}
{-# LANGUAGE UndecidableInstances      #-}
# if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 706
{-# LANGUAGE PolyKinds                 #-}
# endif

pvp clean module imports

it would be great to explicitly import functions and types.

i mean, given

import Module

a:: M
a = fun

it should generate the following import line

import Module (M, fun)

DataTypes style

Hello!
It would be great to add option to style DataTypes and other structures, for example:

data Point = Point
    { pointX :: Double, pointY :: Double
    , pointName :: String
    } 
    deriving (Show)

should be changed to the following (or simmilar):

data Point = Point { pointX    :: Double
                   , pointY    :: Double
                   , pointName :: String
                   } deriving (Show)

Additional the ability to enable or disable some pretification parts would be very usefull - for example, some users would not like to put types in the same column (like in my example)

Haskell2010 language extension ignored

When adding Haskell2010 to .stylish-haskell.yamls language_extensions field or using a language pragma as shown below

{-# LANGUAGE Haskell2010 #-}

module X where

foo x | Just y <- x = y

stylish-haskell fails to infer the following extensions:

  • PatternGuards
  • NoNPlusKPatterns
  • RelaxedPolyRec
  • EmptyDataDecls
  • ForeignFunctionInterface

and thus fails to parse the Haskell2010 compliant source and emits the following error:

Language.Haskell.Stylish.Parse.parseModule: could not parse x.hs: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 6, srcColumn = 1}) "PatternGuards is not enabled"

Parse errors end up in stdout

Today, while piping my xmonad.hs through stylish-haskell in vim I noticed an odd thing. If I write certain syntactically invalid Haskell code the parse errors end up in stylish-haskell's output on stdout.

Here a is session first showing the output on stdout, then on stderr. Also notice that the input file is also printed to stderr.

% stylish-haskell -V
stylish-haskell-0.5.8.0
% stylish-haskell < ~/.xmonad/xmonad.hs 2> /dev/null | head
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 12, srcColumn = 1}) "TemplateHaskell is not enabled"
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 11, srcColumn = 53}) "Parse error: first"
--
-- xmonad(1) configuration
--
-- Relevant documentation:
-- http://xmonad.org/xmonad-docs/
-- http://hackage.haskell.org/package/base
-- http://hackage.haskell.org/package/containers
-- http://hackage.haskell.org/package/xmonad-extras
% stylish-haskell < ~/.xmonad/xmonad.hs > /dev/null | head
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 2, srcColumn = 1}) "TemplateHaskell is not enabled"
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 12, srcColumn = 1}) "TemplateHaskell is not enabled"
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 11, srcColumn = 53}) "Parse error: first"
--
-- xmonad(1) configuration
--
-- Relevant documentation:
-- http://xmonad.org/xmonad-docs/
-- http://hackage.haskell.org/package/base
-- http://hackage.haskell.org/package/containers
-- http://hackage.haskell.org/package/xmonad-extras

Interrestingly stylish-haskell's output is different, if I omit piping it through head. And I don't mean the consequently longer line count. 😃

% stylish-haskell -V
stylish-haskell-0.5.8.0
% stylish-haskell < ~/.xmonad/xmonad.hs 2> /dev/null
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 12, srcColumn = 1}) "TemplateHaskell is not enabled"
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 11, srcColumn = 53}) "Parse error: first"
--
-- xmonad(1) configuration
--
-- Relevant documentation:
-- http://xmonad.org/xmonad-docs/
-- http://hackage.haskell.org/package/base
-- http://hackage.haskell.org/package/containers
-- http://hackage.haskell.org/package/xmonad-extras
--

import           Control.Arrow                      first, (***))
import           Control.Monad                      ((<=<))
import           Data.List                          (isInfixOf)
import qualified Data.Map                           as M
[rest of xmonad.hs]
% stylish-haskell < ~/.xmonad/xmonad.hs > /dev/null
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 2, srcColumn = 1}) "TemplateHaskell is not enabled"

I wasn't able to quickly find a minimal example which would trigger the bug, so here is my whole XMonad configuration. Notice the missing ( in the first import statement.

Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 12, srcColumn = 1}) "TemplateHaskell is not enabled"
Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 11, srcColumn = 53}) "Parse error: first"
--
-- xmonad(1) configuration
--
-- Relevant documentation:
-- http://xmonad.org/xmonad-docs/
-- http://hackage.haskell.org/package/base
-- http://hackage.haskell.org/package/containers
-- http://hackage.haskell.org/package/xmonad-extras
--

import           Control.Arrow                      first, (***))
import           Control.Monad                      ((<=<))
import           Data.List                          (isInfixOf)
import qualified Data.Map                           as M
import           Data.Maybe                         (fromMaybe)
import           Data.Monoid                        (mconcat)
import           System.IO                          (Handle, hPutStrLn)
import           XMonad
import           XMonad.Actions.CycleWS
import           XMonad.Actions.FloatKeys
import           XMonad.Actions.FloatSnap
import           XMonad.Actions.RotSlaves
import           XMonad.Actions.SpawnOn
import           XMonad.Actions.Warp                (Corner (..), banish)
import           XMonad.Actions.WindowBringer
import           XMonad.Hooks.CurrentWorkspaceOnTop
import           XMonad.Hooks.DynamicLog
import           XMonad.Hooks.EwmhDesktops
import           XMonad.Hooks.ManageDocks
import           XMonad.Hooks.ManageHelpers
import           XMonad.Hooks.Place
import           XMonad.Hooks.SetWMName
import           XMonad.Hooks.UrgencyHook
import           XMonad.Layout.Decoration           (Theme (..), defaultTheme)
import           XMonad.Layout.LayoutHints
import           XMonad.Layout.NoBorders
import           XMonad.Layout.PerWorkspace
import           XMonad.Layout.ResizableTile
import           XMonad.Layout.TrackFloating
import qualified XMonad.StackSet                    as W
import           XMonad.Util.Dmenu                  (menuArgs)
import           XMonad.Util.EZConfig               (checkKeymap, mkKeymap)
import           XMonad.Util.Replace
import           XMonad.Util.Run                    (spawnPipe)

main :: IO ()
main = do
    replace
--  handle <- safeSpawnPipe "dzen2"
--            [ "-dock"
--            , "-fn", fontName myTheme
--            , "-fg", inactiveTextColor myTheme
--            , "-bg", inactiveColor myTheme
--            , "-xs", "1"
--            ]
    dir    <- getXMonadDir
    handle <- safeSpawnPipe "xmobar"
              [ "-f", fontName myTheme
              , "-F", inactiveTextColor myTheme
              , "-B", inactiveColor myTheme
              , "--", dir ++ "/xmobarrc"
              ]
    let myConfig = defaultConfig
            { terminal           = "urxvtc"
            , focusFollowsMouse  = True
            , borderWidth        = 1
            , modMask            = mod4Mask
            , workspaces         = myWorkspaces
            , normalBorderColor  = inactiveBorderColor myTheme
            , focusedBorderColor = activeBorderColor myTheme
            , keys               = \c -> mkKeymap c (myKeys c)
            , mouseBindings      = M.fromList . myMouse
--          , layoutHook         = fullscreenFull $ myLayout
            , layoutHook         =
                trackFloating
                . layoutHintsWithPlacement (0.5, 0.5)
                . avoidStruts
                . lessBorders OnlyFloat
                $ myLayout
            , handleEventHook    = mconcat
                [ docksEventHook
                , ewmhDesktopsEventHook
                , fullscreenEventHook
                , hintsEventHook
                ]
            , manageHook         = myManageHook
            , logHook            =
                currentWorkspaceOnTop
                >> (dynamicLogWithPP $ myPP xmobarColor handle)
                >> ewmhDesktopsLogHook
--              >> updatePointer (Relative 1 1)
            , startupHook        =
                return ()
                >> checkKeymap myConfig (myKeys $ myConfig { layoutHook = Layout $ layoutHook myConfig })
                >> ewmhDesktopsStartup
                >> setWMName "LG3D"
                >> spawn "systemd-notify --ready"
            }
    xmonad $ withUrgencyHookC BorderUrgencyHook { urgencyBorderColor = urgentBorderColor myTheme }
                              urgencyConfig     { suppressWhen       = Focused                   }
                              myConfig

myKeys :: XConfig Layout -> [(String, X ())]
myKeys conf =
    -- focus, move, resize
    [ ("M-h"            , rotAllUp)
    , ("M-j"            , windows W.focusDown >> whenX isFloating (windows W.shiftMaster))
    , ("M-k"            , windows W.focusUp   >> whenX isFloating (windows W.shiftMaster))
    , ("M-l"            , rotAllDown)
    , ("M-m"            , windows W.focusMaster)
    , ("M-g"            , nextScreen)
    , ("M-S-h"          , move L)
    , ("M-S-j"          , move D)
    , ("M-S-k"          , move U)
    , ("M-S-l"          , move R)
    , ("M-S-m"          , ifX isFloating (windows W.shiftMaster) (windows W.swapMaster))
    , ("M-S-g"          , shiftNextScreen)
    , ("M-C-h"          , resize L)
    , ("M-C-j"          , resize D)
    , ("M-C-k"          , resize U)
    , ("M-C-l"          , resize R)
    , ("M-C-g"          , swapNextScreen)
    ]
    ++
    (map (("M-" ++)   *** toggleOrView       ) . zip keys . workspaces) conf
    ++
    (map (("M-z " ++) *** (windows . W.shift)) . zip keys . workspaces) conf
    ++
    -- other actions
    [ ("M-q"            , kill)
    , ("M-<F5>"         , refresh)
    , ("M-,"            , sendMessage $ IncMasterN 1)
    , ("M-."            , sendMessage $ IncMasterN (-1))
    , ("M-<Space>"      , sendMessage NextLayout)
    , ("M-S-<Space>"    , setLayout $ layoutHook conf)
    , ("M-t"            , withFocused $ \w -> ifX isFloating (windows . W.sink $ w) (float w))
    , ("M-;"            , spawnHere =<< menuArgs "env" ("SHELL=cat" : "dmenu_run" : dmenuArgs) [])
    , ("M-<F3>"         , gotoMenuArgs $ dmenuArgs ++ ["-i", "-l", "64"])
    -- reclaim focus from flashplugin
    , ("M-<Escape>"     , banish LowerRight >> spawn "xdotool mousemove_relative --sync -- -1 -1 getactivewindow click --clearmodifiers --window %1 2")
    -- copy, paste
    -- This only works on applications which accept events
    -- generated by XSendEvent(3).  Generally with Shift+Insert
    -- GUI toolkits paste from the clipboard, but terminal
    -- emulators paste from the pirmary selection.  In order
    -- to consistently paste from the clipboard we copy its
    -- contents to primary selection before pasting.  Also
    -- remember to setup approriate bindigs for Shift+Insert in
    -- Emacs and gVim.
    , ("M-y"            , spawn "xsel -op | xsel -ib")
    , ("M-p"            , spawn "xsel -ob | xsel -ip; xdotool getactivewindow key --clearmodifiers --window %1 Shift+Insert")
    -- audio, video
    , ("M-<Up>"         , spawn "my-mpc stop")
    , ("M-<Down>"       , spawn "my-mpc toggle")
    , ("M-<Left>"       , spawn "my-mpc prev")
    , ("M-<Right>"      , spawn "my-mpc next")
    , ("M-S-<Up>"       , spawn "my-mpc seek -30")
    , ("M-S-<Down>"     , spawn "my-mpc seek +30")
    , ("M-S-<Left>"     , spawn "my-mpc seek -5")
    , ("M-S-<Right>"    , spawn "my-mpc seek +5")
    , ("<XF86AudioNext>", spawn "my-mpc next")
    , ("<XF86AudioPlay>", spawn "my-mpc toggle")
    , ("<XF86AudioPrev>", spawn "my-mpc prev")
    , ("<XF86AudioStop>", spawn "my-mpc stop")
    , ("<XF86PowerOff>" , spawn "systemctl --user start xdg-screensaver.service")
    ]
    -- spawn clients
    ++ map (first ("M-a " ++))
    [ ("w"  , spawn "systemctl --user start [email protected]")
    , ("S-w", spawn "systemctl --user start chromium.service")
--  , ("C-w", spawn $ "rsync -aE --delete --exclude-from ~/.mozilla/firefox/{exclude.txt,default/,testing} && exec systemctl --user start [email protected]")
    , ("e"  , spawn "systemctl --user start ncmpcpp.service")
    , ("S-e", spawn $ ". \"$XDG_CONFIG_HOME/user-dirs.dirs\"; exec " ++ terminal conf ++ " -cd \"$XDG_MUSIC_DIR\" -name mus")
    , ("r"  , spawn $ ". \"$XDG_CONFIG_HOME/user-dirs.dirs\"; exec " ++ terminal conf ++ " -cd \"$XDG_VIDEOS_DIR\" -name vid")
    , ("s"  , spawn "emacsclient -a /bin/false -c -e '(notmuch)' -F '((menu-bar-lines . 0) (icon-name . \"mail\"))' -n -s \"$XDG_RUNTIME_DIR/emacs\"")
    , ("S-s", spawn "systemctl --user start [email protected]")
    , ("d"  , spawn "systemctl --user start weechat-curses.service")
    , ("f"  , withWindowSet $ spawn . (++) "systemctl --user start urxvtc@" . W.tag . W.workspace . W.current)
    , ("S-f", withWindowSet $ spawn . (++) "systemctl --user start emacsclient@" . W.tag . W.workspace . W.current)
    , ("x"  , spawn "systemctl --user start transmission-remote-cli.service")
    ]
    where keys = words "w e r s d f x c v"

--myLayout :: You don't want to know.
myLayout =
    onWorkspace "www" (Full ||| tiled ||| Mirror tiled) $
    onWorkspaces (words "dl im vid") (Mirror tiled ||| Full ||| tiled) $
    tiled ||| Full ||| Mirror tiled
    where
        tiled   = ResizableTall nmaster delta ratio []
        nmaster = 1
        ratio   = 1/2
        delta   = 3/100

myManageHook :: ManageHook
myManageHook = mconcat
    -- fullscreen
    [ composeOne . map (-?> doFullFloat) $
        [ isFullscreen
        , stringProperty "WM_WINDOW_ROLE" =? "presentationWidget"
        ]
    -- float
    , placeHook $ withGaps (19, 0, 0, 0) $ smart (0.5, 0.5)
    , composeOne . map (-?> doFloat) $
        [ isDialog
        , appName   =? "explorer.exe" <&&> className =? "Wine"
        , appName   =? "sdlmame" <&&> className =? "sdlmame"
        , className =? "Basicwin"  -- CGoban
        , className =? "Kioexec"
        , className =? "MPlayer"
        , title     =? "ePSXe - Enhanced PSX emulator"
        ]
    -- swap
    -- TODO: shift urxvtc -e man ... to master
--  , insertPosition Below Newer
    -- shift
    , manageSpawn
    , composeOne . (:) transience . map (\(w, q) -> q -?> doShift w) $
        [ ("dl" , appName =? "Download"  <&&> className =? "Firefox")
        , ("www", appName =? "chromium"  <&&> className =? "Chromium" <&&> stringProperty "WM_WINDOW_ROLE" =? "browser")
        , ("www", appName =? "Addons"    <&&> className =? "Firefox"  <&&> stringProperty "WM_WINDOW_ROLE" =? "Compatibility")
        , ("www", appName =? "Navigator" <&&> className =? "Firefox")
        ]
        ++ map (\w -> (w, stringProperty "WM_ICON_NAME" =? w       <&&> className =? "Emacs")) myWorkspaces
        ++ map (\w -> (w, appName                       =? w       <&&> className =? "URxvt")) myWorkspaces
        ++ map (\w -> (w, appName                       =? "xterm" <&&> className =? w      )) myWorkspaces
    -- ignore
    , composeOne . map (-?> doIgnore) $
        [ checkDock
        , isKDETrayWindow
        , className =? "Docker"
        , className =? "stalonetray"
        , className =? "Xfce4-notifyd"
        ]
    ]

myMouse :: XConfig l -> [((KeyMask, Button), Window -> X ())]
myMouse (XConfig {modMask = m}) =
    [ ((m, button1), \w -> focus w >> mouseMoveWindow w
                                   >> snapMagicMove margin margin w
                                   >> windows W.shiftMaster)
    , ((m, button2), \w -> focus w >> windows W.shiftMaster)
    , ((m, button3), \w -> focus w >> mouseResizeWindow w
                                   >> snapMagicResize [D, R] margin margin w
                                   >> windows W.shiftMaster)
    ]
    where margin = Just . fromIntegral . decoHeight $ myTheme

myPP :: (String -> String -> String -> String) -> Handle -> PP
myPP coloring handle = defaultPP
    { ppCurrent = coloring (activeTextColor myTheme) (activeColor   myTheme)
    , ppVisible = coloring (activeTextColor myTheme) (inactiveColor myTheme)
    , ppUrgent  = coloring (urgentTextColor myTheme) (urgentColor   myTheme)
    , ppWsSep   = " "
    , ppSep     = "  ::  "
    , ppLayout  = const ""
    , ppTitle   = id
    , ppOutput  = hPutStrLn handle
    }

myTheme :: Theme
myTheme = defaultTheme
    { activeBorderColor   = "Red"
    , activeColor         = "Red"
    , activeTextColor     = "Black"
    , decoHeight          = 16
    , fontName            = "-*-dina-medium-r-*-*-13-*-*-*-*-*-*-*"
    , inactiveBorderColor = "Black"
    , inactiveColor       = "Black"
    , inactiveTextColor   = "White"
    , urgentBorderColor   = "Yellow"
    , urgentColor         = "Yellow"
    , urgentTextColor     = "Black"
    }

myWorkspaces :: [String]
myWorkspaces    = let ws = words "www mus vid mail im ctrl dl msc work"
                  in  ws ++ map show [(length ws + 1) .. 9]

-- utitity functions -----------------------------------------------------------
dmenuArgs :: [String]
dmenuArgs = [ "-fn", fontName myTheme
            , "-nf", inactiveTextColor myTheme
            , "-nb", inactiveColor myTheme
            , "-sf", inactiveTextColor myTheme
            , "-sb", inactiveColor myTheme
            ]

ifX :: X Bool -> X a -> X a -> X a
ifX x t f = x >>= \b -> if b then t else f

isFloating :: X Bool
isFloating = withWindowSet $ \w -> return . fromMaybe False .
    (return . flip M.member (W.floating w) . W.focus
    <=< W.stack . W.workspace . W.current) $ w

move :: Direction2D -> X ()
move d = case d of
              U -> m ( 0, -1) W.swapUp
              D -> m ( 0,  1) W.swapDown
              L -> m (-1,  0) W.swapUp
              R -> m ( 1,  0) W.swapDown
    where
        m d' f = ifX isFloating
                     (withFocused $ keysMoveWindow (d' `sm` decoHeight myTheme))
                     (windows f)

resize :: Direction2D -> X ()
resize d = case d of
                U -> r ( 0, -1) Shrink       MirrorExpand
                D -> r ( 0,  1) Expand       MirrorShrink
                L -> r (-1,  0) MirrorExpand Shrink
                R -> r ( 1,  0) MirrorShrink Expand
    where
        r d' m1 m2 = ifX isFloating
                         (withFocused $ keysResizeWindow (d' `sm` decoHeight myTheme) (0, 0))
                         (ifX (withWindowSet $ return . isInfixOf "Mirror"
                                                      . description
                                                      . W.layout
                                                      . W.workspace
                                                      . W.current)
                              (sendMessage m1)
                              (sendMessage m2))

safeSpawnPipe :: MonadIO m => FilePath -> [String] -> m Handle
safeSpawnPipe x = spawnPipe . unwords . map shellQuote . (:) x

shellQuote :: String -> String
shellQuote s = '\'' : quote s
    where
        quote []        = "\'"
        quote ('\'':cs) = '\'':'\\':'\'':'\'' : quote cs
        quote (c:cs)    = c : quote cs

-- scalar multiplication
sm :: Num a => (a, a) -> a -> (a, a)
sm (x, y) z = (x * z, y * z)

-- vim: foldclose=all foldlevelstart=0 foldopen=all

Bad module formatting with long module names

Consider the following

import qualified Control.Exception                            as E
import           Control.Monad
import           Control.Monad.Error
import           Database.MongoDB                             (Action, Pipe,
                                                               Secs, access,
                                                               close, connect',
                                                               master,
                                                               readHostPortM,
                                                               runIOE)
import qualified Database.MongoDB                             as BSON

import qualified Network.HTTP.Conduit.OAuth                   as Oa
import qualified Network.HTTP.Conduit.OAuth.Types             as Oa
import qualified Network.HTTP.Conduit.OAuth.Types.Credentials as Cred

It'd be nice if there were a way to override the module import formats to align differently in circumstances like this. Otherwise, the result is pretty ugly and unreadable.

MultiWayIf option support

code:

defaultOptions = Options {
    optPlatform = if | os `elem` ["win32", "mingw32", "cygwin32"]  "Win"
                     | os `elem` ["darwin"]  "Mac"
                     | otherwise -> "Linux"

log:

D:\Heather\Contrib\haskell\Cr\src>stylish-haskell.exe -vi Cr.hs
D:\Heather\Contrib\haskell\Cr\src\.stylish-haskell.yaml exists
Loading configuration at D:\Heather\Contrib\haskell\Cr\src\.stylish-haskell.yaml
Enabled Imports step
Enabled LanguagePragmas step
Enabled Records step
Enabled TrailingWhitespace step
Extra language extensions: []
Language.Haskell.Stylish.Parse.parseModule: could not parse Cr.hs: ParseFailed (SrcLoc {srcFilename
= "<unknown>.hs", srcLine = 30, srcColumn = 22}) "Parse error: |"

hangs on PolyKinds or DataKinds

doesn't seem to understand those language flags
likewise: doesn't seem to be very clear how to run it!

(ie the docs are lacking on that piece )

Treatment of long module names

Module names can get very long. Unfortunately, when this happens, stylish-haskell tries to align all of the import lists, "hiding", and "as" clauses to the end of the longest module name. This means much of the content of the import is squished all in the last ~10 columns of the line, leading to far more lines being required than necessary.

The easiest way to fix this would be to teach stylish-haskell to ignore module names over a certain length when choosing the column to align imports to.

Parse error: $

happened on UTF8 without BOM code:

getLastVersionForPlatform :: [Char]  IO String
getLastVersionForPlatform s = withSocketsDo
    $   let url = "http://commondatastorage.googleapis.com/chromium-browser-snapshots/" ++ s ++ "/LAST_CHANGE"
        in simpleHttp url
            >>= \bs  return $ S.decode $ L.unpack bs

log:

D:\Heather\Contrib\haskell\Cr\src>stylish-haskell.exe -vi Google.hs
D:\Heather\Contrib\haskell\Cr\src\.stylish-haskell.yaml exists
Loading configuration at D:\Heather\Contrib\haskell\Cr\src\.stylish-haskell.yaml
Enabled Imports step
Enabled LanguagePragmas step
Enabled Records step
Enabled TrailingWhitespace step
Extra language extensions: []
Language.Haskell.Stylish.Parse.parseModule: could not parse Google.hs: ParseFailed (SrcLoc {srcFile
ame = "<unknown>.hs", srcLine = 26, srcColumn = 32}) "Parse error: $"

Alignment of record fields

I do this a lot with my code. If you specify a record type on multiple lines (or maybe it makes it multi-line if you don't), it could align the ::s and types. For example:

data Foo = Foo { field1 :: Integer,  anotherField :: String }

becomes

data Foo = Foo { field1       :: Integer,
                 anotherField :: String }

That looks horrible in this comment editor but the preview has it looking right.

Automatic removal (addition?) of LANGUAGE pragmas

For some extensions like ViewPatterns or BangOperators one can actually decide from the AST whether they are required or not. For other's like FlexibleInstances more effort is required.

It would be cool, if extensions whose necessity can be decided from the source code, would be removed automatically. This would alleviate the extension creep during the development of a project.

One could even think of automatically adding the extensions, as haskell-src-exts can determine for some whether they are required to parse the code or not.

ParseFailed on {-# LANGUAGE

Language.Haskell.Stylish.Parse.parseModule: could not parse Google.hs: ParseFailed (SrcLoc {srcFilen
ame = ".hs", srcLine = 1, srcColumn = 4}) "Parse error: {-# LANGUAGE"

source:

{-# LANGUAGE UnicodeSyntax #-}
module Google
  ( getLastVersionForPlatform
  , getChromium
  ) where

output

D:\Heather\Contrib\haskell\Cr\src>stylish-haskell.exe -vi Google.hs
D:\Heather\Contrib\haskell\Cr\src\.stylish-haskell.yaml exists
Loading configuration at D:\Heather\Contrib\haskell\Cr\src\.stylish-haskell.yaml
Enabled Imports step
Enabled LanguagePragmas step
Enabled Records step
Enabled TrailingWhitespace step
Extra language extensions: []
Language.Haskell.Stylish.Parse.parseModule: could not parse Google.hs: ParseFailed (SrcLoc {srcFilen
ame = "<unknown>.hs", srcLine = 1, srcColumn = 4}) "Parse error: {-# LANGUAGE"

file was UTF-8

Converted to UTF-8 without BOM, now works...

I'm not sure if that should already works on UTF8 with bom according this commit 3598ec2

Also maybe this issue should be merged with #3

Fails to parse record punning with a qualified name

I'm getting

Language.Haskell.Stylish.Parse.parseModule: could not parse : ParseFailed (SrcLoc {srcFilename = ".hs", srcLine = 353, srcColumn = 35}) "Illegal qualified name"

for the following line of code:

cotile = tile {Kind.ospeedup}

which otherwise works perfectly in GHC.

`stylish-haskell` rejects source with indented module scope

The program below is rejected by stylish-haskell

module Blah where

  import Prelude (IO,return)

  main :: IO ()
  main = do
      return ()

with the parse error

Language.Haskell.Stylish.Parse.parseModule: could not parse Blah.hs: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 5, srcColumn = 3}) "Parse error: main"

whereas ghc happily accepts that program.

Bump `haskell-src-exts` for more extensions

I just bumped haskell-src-exts dependency in my local copy and it now supports MultiWayIf and some other extension. As someone who uses MultiWayIf frequently, this is great.

Unusual Record Formatting (hard tabs)

In the example

data Customer = Customer {
    customerID :: CustomerID,
    customerName :: String,
    customerAddress :: Address
} deriving Show

stylish yields

data Customer = Customer {
    customerID :: Cust     omerID,
    customerName :: Stri   ng,
    customerAddress :: Address
} deriving Show

In Utrecht style

data Customer = Customer {
      customerID :: CustomerID
    , customerName :: String
    , customerAddress :: Address
    } deriving Show

stylish yields

data Customer = Customer {
      customerID :: Cust     omerID
    , customerName :: Stri   ng
    , customerAddress :: Address
    } deriving Show

My global config contains

- records: {}

It appears that this is only an issue when using hard tabs. Using soft tabs yields the expected results. While this is more correct w.r.t. the Report, it may be worthwhile to add support for tabs, or provide a warning in the README about this case.

Sorting with case insensitivity

If I have

import Happstack.Server
import HSP

stylish-haskell will sort it as

import HSP
import Happstack.Server

This is contrary to my expectation and wishes.

Treatment of long import lists

If I have

import Happstack.Server              (Happstack, Response, neverExpires, setHeaderM, badRequest, ok, toResponse, guessContentTypeM, mimeTypes, lookPairs)

stylish-haskell will produce

import Happstack.Server              (Happstack, Response, badRequest, guessContentTypeM, lookPairs,
 mimeTypes, neverExpires, ok, setHeaderM, toResponse)

My preference would be to either keep the import list on one line, or line up wrapped lines with the parenthesis:

import Happstack.Server              (Happstack, Response, badRequest, guessContentTypeM, lookPairs
                                     ,mimeTypes, neverExpires, ok, setHeaderM, toResponse)

Installation uses some deprecated compilation options

Too many to note now, but many dependencies use -fglasgow-exts,
haskell-src-exts uses an old interface of runTests,
resourcet shadows the binding Control.Monad.Reader.Class.reader,
etc. etc.
The full list can be found by running cabal update && cabal install stylish-haskell
Unfortunately, I didn't have the foresight to pipe the output of cabal install to a file,
and can't undo the installation. Hopefully, this description is detailled enough.
Please forward upstream.

Parse error with DataKinds

stylish-haskell on

{-# LANGUAGE DataKinds, KindSignatures, TypeOperators, GADTs #-}

module Test where

data HList :: [*] -> * where
  HNil  :: HList '[]
  HCons :: a -> HList t -> HList (a ': t)

fails with

Language.Haskell.Stylish.Parse.parseModule: could not parse Test.hs: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 5, srcColumn = 15}) "Parse error: ["

Break overly-long lines in type constructor list in import list

Example:

import Language.Haskell.Syntax (HsExportSpec(HsEVar, HsEAbs, HsEThingAll, HsEThingWith, HsEModuleContents)

should be converted to

import Language.Haskell.Syntax (HsExportSpec(HsEVar, HsEAbs, HsEThingAll,
                                             HsEThingWith, HsEModuleContents)

(Note that I'm starting a new line after the last constructor which is still within 80 chars
from the end of the line.)

Configuration

We want flexible configuration with an easy syntax. For this purpose, we'll probably use a YAML config file.

The config file is loaded from:

  1. If the user passes --config FILE to the tool, we use that.
  2. If the current directory contains a .stylish-haskell.yaml file, pick that -- this allows per-project settings.
  3. (I'm not sure on this one: should we try to load .stylish-haskell.yaml from the user's home directory?)
  4. Use the defaults.

TemplateHaskell QuasiQuote splicing causes errors in certain cases

This is likely an upstream problem, but parsing fails when splicing into quasiquations which look like other declarations.

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE QuasiQuotes       #-}
{-# LANGUAGE TemplateHaskell   #-}

import           Language.Haskell.TH

x :: DecsQ
x = [d| instance Show $(conT (mkName "Int")) |]

Class arg type parse error

Hello, I've got parse error while prsing following WORKING Haskell code:

[...]
class Member (name :: Symbol) cls func | name cls -> func where 
    member :: proxy name -> cls -> func
[...]

error:

Language.Haskell.Stylish.Parse.parseModule: could not parse Data.hs: ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 29, srcColumn = 23}) "Parse error: Symbol"

long import split across two lines

I got "pulled" into stylish-haskell via haskell-mode.git...

stylish-haskell is giving me:

import Distribution.PackageDescription.Configuration
                                                      (finalizePackageDescription)

instead of:

import Distribution.PackageDescription.Configuration  (finalizePackageDescription)

Not quite sure why it is breaking the import across a line given the second is intended further anyway.

Also stylize LANGUAGE pragmas

First, thanks for this neat tool. I've been using it to cleanup my imports and it works like a charm. (I don't have many multi-line imports ;-)).

I think it would be great, if we could push this further towards a Haskell stylizer. As a first step, I'd go for also stylizing LANGUAGE pragmas. I'd expect the following behaviour.

{-# LANGUAGE TypeOperators, StandaloneDeriving, DeriveDataTypeable, TemplateHaskell #-}
{-# LANGUAGE TemplateHaskell, ViewPatterns #-}

is converted to

{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving, TemplateHaskell,
             TypeOperators,  ViewPatterns
  #-}

i.e., the following steps happen:

  1. All LANGUAGE pragmas are gathered
  2. The list of pragmas is sorted and duplicates are removed
  3. The list is rendered at the position of the first LANGUAGE pragma. The format is such that a single LANGUAGE pragma is used and the extensions are wrapped around on 80 chars per line as in the example above.

Comments should not be stripped from 'import' statements and language pragmas

Consider:

module Foo where
import Data.Monoid -- from base
import Data.List   -- from base

And then:

$ stylish-haskell /tmp/Foo.hs
module Foo where
import           Data.List
import           Data.Monoid
$

The comments shouldn't be stripped from the end, ideally. At the very least this is inconsistent with the default behavior which will preserve comments in most other positions.

Fails to parse view patterns combined with bang patterns

I have the following line in one of my source files:

offsetElems (fromIntegral -> !off) tri = tri { triIndices = Vector.map (+off) $ triIndices tri }

stylish-haskell fails with this message:

    Language.Haskell.Stylish.Parse.parseModule: could not parse <unknown>:
    ParseFailed (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 154,
    srcColumn = 38}) "Parse error: !"

Feature Request: Plain Stylish Imports

It would be nice to have an option for import styling that alphabetized imports, but just did a sort of flat alignment. E.g., it would take

import qualified System.Directory   as Dir
import     Control.Applicative       ((<$>))

to

import Control.Applicative ((<$>))
import qualified System.Directory as Dir

PackageImport disappearing

{-# LANGUAGE PackageImports      #-}
import "mtl" Control.Monad.Trans

and after stylish

{-# LANGUAGE PackageImports #-}
import           Control.Monad.Trans

Import and export lists are inconsistent

I don't put a space before the parenthesis in things like Applicative(..). stylish adds one in imports, but leaves my module exports unaffected, leading to style inconsistency in the same file.

I'd like:

  • An option to not have this space added
  • The same style used in exports and imports, in either configuration

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.