Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
Self-hosting: project contains test cases which validate formatting
of the source files in the project.
  • Loading branch information
danstiner committed Dec 3, 2014
0 parents commit fc33859
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/dist
/cabal-dev
*.o
*.hi
*.chi
*.chs.h
*.tmp
.virthualenv
*.sublime-workspace
.hsenv
.cabal-sandbox/
cabal.sandbox.config
cabal.config
77 changes: 77 additions & 0 deletions .stylish-haskell.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# stylish-haskell configuration file
# ==================================

# The stylish-haskell tool is mainly configured by specifying steps. These steps
# are a list, so they have an order, and one specific step may appear more than
# once (if needed). Each file is processed by these steps in the given order.
steps:
# Convert some ASCII sequences to their Unicode equivalents. This is disabled
# by default.
# - unicode_syntax:
# # In order to make this work, we also need to insert the UnicodeSyntax
# # language pragma. If this flag is set to true, we insert it when it's
# # not already present. You may want to disable it if you configure
# # language extensions using some other method than pragmas. Default:
# # true.
# add_language_pragma: true

# Import cleanup
- imports:
# There are different ways we can align names and lists.
#
# - global: Align the import names and import list throughout the entire
# file.
#
# - file: Like global, but don't add padding when there are no qualified
# imports in the file.
#
# - group: Only align the imports per group (a group is formed by adjacent
# import lines).
#
# - none: Do not perform any alignment.
#
# Default: global.
align: file

# Language pragmas
- language_pragmas:
# We can generate different styles of language pragma lists.
#
# - vertical: Vertical-spaced language pragmas, one per line.
#
# - compact: A more compact style.
#
# - compact_line: Similar to compact, but wrap each line with
# `{-#LANGUAGE #-}'.
#
# Default: vertical.
style: vertical

# stylish-haskell can detect redundancy of some language pragmas. If this
# is set to true, it will remove those redundant pragmas. Default: true.
remove_redundant: true

# Align the types in record declarations
- records: {}

# Replace tabs by spaces. This is disabled by default.
- tabs:
# # Number of spaces to use for each tab. Default: 8, as specified by the
# # Haskell report.
spaces: 2

# Remove trailing whitespace
- trailing_whitespace: {}

# A common setting is the number of columns (parts of) code will be wrapped
# to. Different steps take this into account. Default: 80.
columns: 80

# Sometimes, language extensions are specified in a cabal file or from the
# command line instead of using language pragmas in the file. stylish-haskell
# needs to be aware of these, so it can parse the file correctly.
#
# No language extensions are enabled by default.
# language_extensions:
# - TemplateHaskell
# - QuasiQuotes
38 changes: 38 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
language: haskell

env:
- GHCVER=7.6.3
- GHCVER=7.8.2
- GHCVER=head

matrix:
fast_finish: true
allow_failures:
- env: GHCVER=head

cache:
directories:
- $HOME/.cabal
- $HOME/.ghc

before_install:
- sudo add-apt-repository -y ppa:hvr/ghc
- sudo add-apt-repository -y "deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ trusty main"
- sudo apt-get update
- sudo apt-get install cabal-install-1.20 ghc-$GHCVER
- sudo apt-get install libleveldb-dev -t trusty

install:
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/1.20/bin:$PATH
- cabal --version
- cabal update
- cabal install -j --enable-tests --enable-benchmarks --only-dependencies

script:
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/1.20/bin:$PATH
- cabal configure --enable-tests --enable-benchmarks -v2
- cabal build
- cabal test
- cabal check
- cabal sdist

22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2014 Daniel Stiner

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.

2 changes: 2 additions & 0 deletions Setup.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain
56 changes: 56 additions & 0 deletions hfmt.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: hfmt
version: 0.0.0.1
synopsis: Hfmt formats Haskell programs.
description: Hfmt formats Haskell programs. Inspired by gofmt, built on hlint and stylish-haskell
license: MIT
license-file: LICENSE
author: Daniel Stiner
maintainer: Daniel Stiner <[email protected]>
stability: Experimental
homepage: http://github.com/danstiner/hfmt
bug-reports: http://github.com/danstiner/hfmt/issues
category: Language
build-type: Simple
cabal-version: >=1.9.2
tested-with: GHC==7.6.3, GHC==7.8.2

executable hfmt
Hs-Source-Dirs: src
Main-Is: Main.hs
Build-Depends: base == 4.*
, haskell-src-exts
, hlint >= 1.9 , hlint < 2
, optparse-applicative
, stylish-haskell
, Glob

library
Hs-Source-Dirs: src
Exposed-Modules: Language.Haskell.Format
Language.Haskell.Format.Tests
Build-Depends: base == 4.*
, haskell-src-exts
, hlint >= 1.9.13 , hlint < 2
, stylish-haskell
, HUnit
, Glob

test-suite test
Type: exitcode-stdio-1.0
Hs-Source-Dirs: src test
Main-Is: TestSuite.hs
Build-Depends: base == 4.*
, haskell-src-exts
, hlint >= 1.9 , hlint < 2
, stylish-haskell
, Glob

, HUnit
, QuickCheck >= 2.7
, test-framework
, test-framework-hunit
, test-framework-quickcheck2

source-repository head
type: git
location: git://github.com:danstiner/transpiler.git
9 changes: 9 additions & 0 deletions hfmt.sublime-project
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"folders":
[
{
"follow_symlinks": true,
"path": "."
}
]
}
23 changes: 23 additions & 0 deletions src/Language/Haskell/Format.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Language.Haskell.Format (autoSettings, check, checkPath, Stylish.FormatResult (..)) where

import qualified Language.Haskell.Format.HLint as HLint
import qualified Language.Haskell.Format.Stylish as Stylish

import Control.Applicative
import Language.Haskell.HLint3 (Idea)

data Settings = Settings { unHlintSettings :: HLint.Settings, unStylishSettings :: Stylish.Settings }

type CheckResult = ([Idea], Stylish.FormatResult)

autoSettings :: IO Settings
autoSettings = Settings <$> HLint.autoSettings <*> Stylish.autoSettings

checkPath :: Settings -> FilePath -> IO (Either String CheckResult)
checkPath settings path = readFile path >>= check settings (Just path)

check :: Settings -> Maybe FilePath -> String -> IO (Either String CheckResult)
check settings path contents = do
hlint <- HLint.check (unHlintSettings settings) path contents
stylish <- Stylish.check (unStylishSettings settings) path contents
return $ (\a b -> (a,b)) <$> hlint <*> stylish
24 changes: 24 additions & 0 deletions src/Language/Haskell/Format/HLint.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Language.Haskell.Format.HLint (check, Settings, autoSettings) where

import Data.Maybe
import Language.Haskell.HLint3 (Classify, Hint, Idea, ParseFlags)
import qualified Language.Haskell.HLint3 as HLint

data Settings = Settings ParseFlags [Classify] Hint

autoSettings :: IO Settings
autoSettings = do
(parseFlags, classifications, hint) <- HLint.autoSettings
return (Settings parseFlags classifications hint)

check :: Settings -> Maybe FilePath -> String -> IO (Either String [Idea])
check settings mPath contents = do
parsed <- HLint.parseModuleEx parseFlags (fromMaybe "" mPath) (Just contents)
return $ case parsed of
Left parseError -> Left (showParseError parseError)
Right parseResult -> Right $ HLint.applyHints classifications hint [parseResult]
where
Settings parseFlags classifications hint = settings

showParseError :: HLint.ParseError -> String
showParseError = HLint.parseErrorMessage
23 changes: 23 additions & 0 deletions src/Language/Haskell/Format/Stylish.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Language.Haskell.Format.Stylish (check, Settings, FormatResult (..), autoSettings) where

import Control.Applicative
import Language.Haskell.Stylish as Stylish

data FormatResult = FormatResult String String
data Settings = Settings Stylish.Config

autoSettings :: IO Settings
autoSettings = Settings <$> Stylish.loadConfig (Stylish.makeVerbose False) Nothing

check :: Settings -> Maybe FilePath -> String -> IO (Either String FormatResult)
check settings path contents =
case runResult of
Left err -> return $ Left err
Right formattedLines -> ret formattedLines
where
runResult = Stylish.runSteps extensions path steps (lines contents)
ret :: [String] -> IO (Either String FormatResult)
ret = return . Right . FormatResult contents . unlines
(Settings config) = settings
extensions = Stylish.configLanguageExtensions config
steps = Stylish.configSteps config
27 changes: 27 additions & 0 deletions src/Language/Haskell/Format/Tests.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Language.Haskell.Format.Tests (hunit, hunitGlob) where

import Control.Monad
import Language.Haskell.Format as Format
import System.FilePath.Glob (glob)
import Test.HUnit

type FilePathGlob = String

hunit :: [FilePath] -> Test
hunit = TestList . map hunitTestcase

hunitGlob :: FilePathGlob -> IO Test
hunitGlob pattern = fmap (TestList . map hunitTestcase) (glob pattern)

hunitTestcase :: FilePath -> Test
hunitTestcase path = TestLabel path . TestCase $ do
settings <- Format.autoSettings
result <- Format.checkPath settings path
case result of
Left err -> assertFailure ("Parse error in: " ++ path ++ "\n" ++ err)
Right (ideas, formatted) -> if not (null ideas)
then assertFailure (foldr ((++) . show) "" ideas)
else assertOnFormatting formatted
where
assertOnFormatting (FormatResult before after) =
when (before /= after) $ assertFailure "Formatting failure"
6 changes: 6 additions & 0 deletions src/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Language.Haskell.HLint3 as HLint
import Language.Haskell.Stylish


main :: IO ()
main = return ()
14 changes: 14 additions & 0 deletions test/TestSuite.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Language.Haskell.Format.Tests as Hfmt

import Test.Framework (defaultMain, testGroup)
import Test.Framework.Providers.HUnit
import Test.HUnit hiding (Test)

main :: IO ()
main = do
src <- hunitGlob "src/**/*.hs"
test <- hunitGlob "test/**/*.hs"
defaultMain [
testGroup "Validate Formatting in src/**/*.hs" (hUnitTestToTests src)
, testGroup "Validate Formatting in test/**/*.hs" (hUnitTestToTests test)
]

0 comments on commit fc33859

Please sign in to comment.