diff --git a/changes.txt b/changes.txt
new file mode 100644
--- /dev/null
+++ b/changes.txt
@@ -0,0 +1,14 @@
+0.1.1.0
+-------
+
+- Released Fri 12 Jun 2015 01:38:26 CEST
+- make flagword syntax more liberal (-- todo something <-- is valid now)
+- more languages available (c++, java, javascript, python, ruby)
+- fixed end-of-file bug and double quote string literal
+
+0.1.0.2
+-------
+
+- Released Tue 09 Jun 2015 21:05:33 CEST
+- Added changes.txt as changeloc
+
diff --git a/doc/usr/page.rst b/doc/usr/page.rst
--- a/doc/usr/page.rst
+++ b/doc/usr/page.rst
@@ -3,8 +3,8 @@
 author: Francesco Ariis
 summary: lentil issue tracker user manual
 tags: lentil,issue,bug,issue tracker,bug tracker,manual
-published: 2015-06-15 00:00:00
-revised: 2015-06-15 00:00:00
+published: 2015-06-09 00:00:00
+revised: 2015-06-10 00:00:00
 --------------------------------------------------------------------------------
 
 
@@ -24,9 +24,11 @@
 time and effort and I like the convenience of having bug reports detailed near
 the offending functions.
 
-``lentil`` helps you with housekeeping, querying, exporting issues without
+`lentil`_ helps you with housekeeping, querying, exporting issues without
 having to modify a single line of code.
 
+.. _`lentil`: http://ariis.it/static/articles/lentil/page.html
+
 .. raw:: html
 
    <p><br/></p>
@@ -34,6 +36,10 @@
 Installation
 ------------
 
+(if you don't want to compile it yourself, `binaries`_ are available)
+
+.. _`binaries`: http://ariis.it/link/repos/lentil/bin/
+
 lentil is written in Haskell, which you can easily grab from your
 OS repository (e.g. for Debian users ``apt-get install
 haskell-platform``). Once you do that, installing lentil is as easy as:
@@ -94,9 +100,25 @@
 Input format
 ------------
 
-lentil parses issues that start with ``TODO:`` and ``FIXE:``, followed by
-a space, followed by freeform text (text can be multiline).
+lentil parses basic ``TODO`` issues. The precise syntax is: keyword (one
+of ``todo``, ``fixme``, ``xxx``), optional semicolon, followed by
+a space, followed by free-form text (text can be multiline). In so many
+words, it is quite liberal in what it accepts:
 
+::
+
+  -- These will all be accepted
+
+  // TODO: explanatory issue
+
+  // todo ehy you left an assert out there!
+
+  //Fixme bad coding style
+
+  /* Xxx: add to version control.
+          Ask Timothy */
+
+
 You can optionally put tags at the end of your issue, like this:
 
 ::
@@ -104,11 +126,21 @@
   // FIXME: Mr. Burns should enter from the *right* side of the
   //        nuclear station [script] [priority:1]
 
-Tags are single words which are useful to catalogue and identify issues.
+Tags are single words which are useful to catalogue and identify issues. Use
+them aplenty as they will make slicing and dicing your issue-base a breeze.
 
-As now lentil parses haskell source files (``.hs``, ``.lhs``), C source
-files (``.c``, ``.h``) and plain text files (``.txt``) and ignores
-everything else.
+As now lentil parses:
+
+- haskell source files (``.hs``, ``.lhs``)
+- C source files (``.c``, ``.h``), C++ (``.cpp``, ``.hpp``), Java (``.java``)
+- javascript source files (``.js``)
+- python source files (``.py``)
+- ruby source files (``.rb``)
+- pascal source files (``.pas``, ``.pp``, ``.inc``)
+- plain text files (``.txt``)
+
+Every other file-type will get ignored! Contact me if you need another
+language to be added.
 
 .. raw:: html
 
diff --git a/issues.txt b/issues.txt
new file mode 100644
--- /dev/null
+++ b/issues.txt
@@ -0,0 +1,78 @@
+doc/dev/issues.txt
+     8  from String to Text? [feature:advanced]
+    10  colorise output on regexps? [feature:advanced]
+    11  allow different markups [feature:advanced]
+    12  recognize module names [feature:advanced]
+    13  have stuff linked to haskell-platform friendly e non template
+        haskell and stuff [feature:intermediate]
+    15  html report, come hlint [feature:advanced]
+    16  symlinks? [feature:intermediate]
+    17  le ricerche è meglio che siano case unsensitive [feature:basic]
+    18  check --output pretty, there are trailing white spaces [bug] [u:1]
+    19  parser crash on non new line terminated file? [bug] [u:3]
+    20  test con 7.6.3 [feature:basic] [test]
+    21  todo/fixmes in different sections [request] [u:3]
+ 
+src/Lentil.hs
+     1  compile with O2? [refactor]
+    12  1st line todo is not recognised [bug]
+    21  clean these nested ifs up [lint]
+ 
+src/Lentil/Args.hs
+    21  show help option in lentil short help [feature:intermediate]
+    23  disambiguation optparse-applicative [feature:intermediate]
+    24  help as lentil PATH [ PATH... ] [ OPTIONS ] [feature:intermediate]
+    51  uncomment and implement sorting [feature:intermediate]
+ 
+src/Lentil/File.hs
+    41  combine funziona su windows? [feature:intermediate]
+ 
+src/Lentil/Parse/Issue.hs
+     8  eliminate lookahead? fai il parser davvero da bottom up senza sub
+        parsing [lint] [refactor]
+    26  streamline parser, indentation doesn't matter now [refactor] [u:3]
+    27  last line non ce la fa se non c'è un "\n" [bug] [u:3]
+    28  make syntax more flexyble (anche todo babaabab) [bug] [u:2]
+    29  bug qk_apostrophe : if holdingshift then paste:='"' else
+        paste:=''''; [bug] [request] [u:3]
+    31  Text instead of String, for utf8 goodness [bug] [input]
+    32  a naked todo should not be recognized an an issue [test]
+    33  handle parsing errors gracefully (not it explodes) (vedi
+        lentilfail) [bug]
+    35  e che fare quando il todo segue un * come nei commenti lunghi c?
+        [bug]
+    62  add this to (ciString ancillaries?) [refactor]
+    71  add blankline to ancillaries? [lint]
+   109  lint `incipit`© [lint]
+   117  allow tags in before description? [doc] [design]
+   142  rimuovi tutti gli eof, oramai non servono [refactor]
+   185  partial pattern! [bug] [refactor] [lint]
+   189  deduplicate runparsource
+ 
+src/Lentil/Parse/Source.hs
+    38  ++ "\n" is inefficient [lint] [refactor]
+    56  fallo diventare string così non impazzisci nelle altre parti
+    78  lint program parser [lint]
+    84  manyTill1 broken? occhio che al primon on chiama ed! [test]
+    98  add signatore to sources & tests [lint] [test]
+   110  add more langparsers [feature:intermediate]
+   112  fallo che sia estensibile e leggibile a compilazione [design] [u:3]
+ 
+src/Lentil/Print.hs
+    41  choose report width (test senza tag)
+ 
+src/Lentil/Query.hs
+     8  filtering case unsensitive? [test]
+ 
+test/Lentil/ArgsSpec.hs
+    10  can't test lOpts, need to make a runner and a validator on the
+        output from io [test]
+ 
+test/Lentil/Parse/IssueSpec.hs
+    43  eof blankline, ma non c'è sempre un \n in blanklines? [test]
+ 
+test/test-files/lang-comm/blocks.txt
+     3  issue one [t] [q:f]
+     5  issue two
+    11  issue three [a]
+    16  issue four
diff --git a/lentil.cabal b/lentil.cabal
--- a/lentil.cabal
+++ b/lentil.cabal
@@ -1,8 +1,9 @@
 name:                lentil
-version:             0.1.0.2
+version:             0.1.1.0
 synopsis:            frugal issue tracker
-description:         minumum effort, cohesive issue tracker for the rest of us.
-                     Check homepage for manual, tutorial.
+description:         minumum effort, cohesive issue tracker based on
+                     ubiquitous @TODO@s and @FIXME@s conventions.
+                     Check homepage for manual, tutorial, examples.
 homepage:            http://www.ariis.it/static/articles/lentil/page.html
 license:             GPL-3
 license-file:        LICENSE
@@ -11,24 +12,37 @@
 copyright:           © 2015 Francesco Ariis
 category:            Development
 build-type:          Simple
-extra-source-files:  README, doc/usr/page.rst, doc/usr/test.zip
+tested-with:         GHC==7.10.1
+extra-source-files:  test/test-files/lang-comm/clang.c,
+                     test/test-files/lang-comm/haskell.hs,
+                     test/test-files/lang-comm/javascript.js,
+                     test/test-files/lang-comm/out.blocks,
+                     test/test-files/lang-comm/pascal.pas,
+                     test/test-files/lang-comm/python.py,
+                     test/test-files/lang-comm/ruby.rb,
+                     test/test-files/lang-comm/test.txt,
+                     test/test-files/lang-comm/text.txt,
+                     test/test-files/test-proj/fold-a/foo1.hs,
+                     test/test-files/test-proj/fold-b/foo2.hs,
+                     test/test-files/test-proj/fold-c/foo3.hs,
+                     test/test-files/test-proj/fold-c/sub-fold/foo4.hs,
+                     test/test-files/test-proj/fold-c/sub-fold/foo5.hs
+extra-doc-files:     README, changes.txt, doc/usr/page.rst, doc/usr/test.zip,
+                     issues.txt
 cabal-version:       >=1.10
 
 
 executable lentil
   main-is:             Lentil.hs
-
-  build-depends:       base >=4.8 && <4.9, optparse-applicative >=0.11 && <0.12,
+  build-depends:       base >=4.7 && <4.9, optparse-applicative >=0.11 && <0.12,
                        regex-tdfa >=1.2 && <1.3, natural-sort >=0.1 && <0.2,
                        parsec >=3.1 && <3.2, filepath >=1.3 && <1.5,
                        directory >=1.2 && <1.3, filemanip >=0.3 && <0.4,
                        ansi-wl-pprint >= 0.6 && < 0.7, csv >= 0.1 && < 0.2
-
   other-modules:       Lentil.Types, Lentil.Args, Lentil.File, Lentil.Print,
-                       Lentil.Query, Lentil.Export, Lentil.Parse
-
+                       Lentil.Query, Lentil.Export, Lentil.Parse.Source,
+                       Lentil.Parse.Issue
   hs-source-dirs:      src
-
   default-language:    Haskell2010
 
 test-suite test
@@ -42,6 +56,12 @@
                        directory >=1.2 && <1.3, filemanip >=0.3 && <0.4,
                        ansi-wl-pprint >= 0.6 && < 0.7, hspec >=1.8,
                        csv >= 0.1 && < 0.2
+  other-modules:       Lentil.Types, Lentil.Args, Lentil.File, Lentil.Print,
+                       Lentil.Query, Lentil.Export, Lentil.Parse.Source,
+                       Lentil.Parse.Issue,
+                       Lentil.ArgsSpec, Lentil.ExportSpec, Lentil.FileSpec,
+                       Lentil.PrintSpec, Lentil.Parse.SourceSpec,
+                       Lentil.Parse.IssueSpec, Lentil.QuerySpec
   type:                exitcode-stdio-1.0
 
 source-repository head
diff --git a/src/Lentil.hs b/src/Lentil.hs
--- a/src/Lentil.hs
+++ b/src/Lentil.hs
@@ -8,8 +8,9 @@
 import Options.Applicative
 
 import qualified Data.Maybe as M
+import Data.Monoid
 
--- TODO: Lentil.Parse.Lentil.Parse.Source.manyTill1 (too many) [bug]
+-- TODO: compile with O2? [refactor]
 
 main :: IO ()
 main = execParser opts >>= runLentil
@@ -27,7 +28,7 @@
                                    short 'v'      <>
                                    help "show version and copyright info" )
     where
-          versionCopy = "lentil - a frugal issue tracker, version 1.0.0\n\
+          versionCopy = "lentil - a frugal issue tracker, version 0.1.1.0\n\
                         \(C) 2015 Francesco Ariis - http://www.ariis.it\n\
                         \released under the GNU General Public License 3"
 
diff --git a/src/Lentil/File.hs b/src/Lentil/File.hs
--- a/src/Lentil/File.hs
+++ b/src/Lentil/File.hs
@@ -11,7 +11,7 @@
 module Lentil.File where
 
 import Lentil.Types
-import Lentil.Parse
+import Lentil.Parse.Issue
 
 import System.FilePath
 import System.FilePath.Find
diff --git a/src/Lentil/Parse.hs b/src/Lentil/Parse.hs
deleted file mode 100644
--- a/src/Lentil/Parse.hs
+++ /dev/null
@@ -1,324 +0,0 @@
------------------------------------------------------------------------------
--- |
--- Module      :  Lentil.Parse.Source
--- Copyright   :  © 2015 Francesco Ariis
--- License     :  GPLv3 (see the LICENSE file)
---
--- Issues parsing from source.
------------------------------------------------------------------------------
-
-module Lentil.Parse where
-
-import Lentil.Types
-
-import Text.Parsec
-
-import Control.Applicative hiding ( (<|>), optional, many )
-
-import qualified Data.Char as C
-import qualified Data.List as L
-import qualified System.FilePath as SF
-import qualified System.Directory as D
-import qualified System.IO as I
-import qualified Data.Either as E
-
-
--- TODO: eliminate lookahead? fai il parser davvero da bottom up senza sub
---       parsing [lint] [refactor] ?
-
-
------------
--- TYPES --
------------
-
-type ParSource = Parsec String ParState
-
-data ParState = ParState { psPath :: FilePath }
-
-
----------------
--- PRIMITIVE --
----------------
-
--- a unix file is defined as many lines (each one ended by '\n')
--- this parser captures the definition and can be used as eof replacement too
-eoft :: ParSource ()
-eoft = optional (char '\n') *> eof
-
--- i.e. remove unneeded whitespace
-htmlify :: String -> String
-htmlify cs = unwords . words $ cs
-
-
--- occhio che il primo carattere non è controllato da ED
--- TODO: manyTill1 broken? occhio che al primon on chiama ed! [test]
-manyTill1 :: ParSource a -> ParSource b -> ParSource [a]
-manyTill1 p ed = (:) <$> p <*> manyTill p ed
-
-
-----------
--- TAGS --
-----------
-
--- simple tags parsing
-
--- tag only
-tag :: ParSource Tag
-tag = Tag <$> (openPar *> tagLabel <* closePar) <?> "tag"
-
--- anything goes, apart from ' '
-tagLabel :: ParSource String
-tagLabel = many1 (satisfy sf) <?> "tag label"
-    where sf :: Char -> Bool
-          sf c | c == closeDel = False
-               | C.isSpace c   = False
-               | otherwise     = True
-
-openPar, closePar :: ParSource Char
-openPar  = char openDel  <?> "open-tag delimiter"
-closePar = char closeDel <?> "close-tag delimiter"
-
-
-------------
--- ISSUES --
-------------
-
--- simple issue parsing
-
--- parses a number of issues from a given line-of-text
-issues :: ParSource [Issue]
-issues = many (try $ manyTill anyChar (lookAhead $ try incipit) *> issue)
-         <?> "issues"
-
--- an issue is a flagword, followed by : , followed by some description and
--- ended optionally by some tags (No t/f? End by whiteline or eof or
--- another TODO).
-issue :: ParSource Issue
-issue = Issue <$> fmap psPath getState
-              <*> fmap sourceLine getPosition
-              <*> (incipit *> fmap htmlify freeText)
-              <*> (optional spaces *> option [] (try tags))
-        <?> "issue"
-
-
--- flagwords + ": "
-incipit :: ParSource ()
-incipit = () <$ (choice (map string flagWords) *> string ": ") <?> "incipit"
-
-flagWords :: [String]
-flagWords = ["TODO", "FIXME", "NOTE", "XXX"]
-
-tags :: ParSource [Tag]
-tags = sepEndBy1 tag spaces <?> "tags"
-
--- any text. Since tags/fields at the end of the issue are optional, we need
--- a way to delimit this. Delimiters are: eof, tags/fields, new issue
-freeText :: ParSource Description
-freeText = manyTill anyChar end <?> "free text"
-    where vp p      = try . parsecMap (const ()) $ p
-          end       = lookAhead $ choice [vp (spaces *> tag),
-                                          vp (spaces *> incipit), vp eoft]
-
-
-------------
--- BLOCKS --
-------------
-
--- blocks parsing, check `block` comment for more info
-
-blocks :: ParSource [Issue]
-blocks = concat <$> many1 block
-
--- a block is some text separated by emptyline(s) (a paragraph). Once you
--- parse it you need to unwrap it and scan for comments. Whitespace
--- is treated like in html.
-block :: ParSource [Issue]
-block = getPosition                                  >>= \lep ->
-        manyTill1 anyChar (try $ emptyline <|> eoft) >>= \rb ->
-        getState                                     >>= \ps ->
-        case runParser (bi lep) ps "<block-parse>" rb of
-            Right r  -> return r
-            Left  l  -> parserFail ("block subp: " ++ show l)
-        <?> "block"
-    where
-          bi p = setPosition p >> issues
-
-
-emptyline :: ParSource ()
-emptyline = () <$ (endOfLine *> many (satisfy ws) *> endOfLine) <?> "emptyline"
-    where ws c = C.isSpace c && notElem c "\n\r\f\v"
-               -- as per definition of isSpace in Data.Char
-
-
---------------
--- COMMENTS --
---------------
-
--- TYPES --
-
--- StringPos is the comment itself + its indentation level. Conventionally
--- multiline comments will have IL of 0 to sever them from single line
--- comments
-type StringPos  = (Int, Int, String) -- Col, Row, String
-
-
--- FUNCTIONS --
-
--- Comment is the same, with just line indication, for comments that go
--- together indentation wise. Check the utility function sps2cr
-type CommentRow = (Int, String) -- row, String
-
-sps2cr :: [[StringPos]] -> [CommentRow]
-sps2cr sps = map sp2cr sps
-    where sp2r (_,r,_) = r
-          sp2s (_,_,s) = s
-
-          sp2cr [] = error "empty group in sps2cr"
-          sp2cr zs = (sp2r . head $ zs, myUnlines . map sp2s $ zs)
-
-          -- do not add unneeded \n at end
-          myUnlines zs = L.intercalate "\n" zs
-
-
--- comments: from a source file to something parsable by `blocks`
--- indent level 0, to recognise them from single line
-multiLine :: String -> String -> ParSource StringPos
-multiLine st ed = getPosition >>= \a ->
-                  (,,) <$> pure 0
-                       <*> pure (sourceLine a)
-                       <*> (string st *> manyTill anyChar (try $ string ed))
-                  <?> "multiline comment"
-
--- returns single line comment and starting column of such comment
-singleLine :: String -> ParSource StringPos -- int: starting col
-singleLine st = getPosition >>= \a ->
-                (string st *> manyTill anyChar (() <$ endOfLine <|> eoft)) >>= \s ->
-                return (sourceColumn a, sourceLine a, s)
-                <?> "singleline comment"
-
-
-rawComment :: String -> String -> String -> ParSource StringPos
-rawComment st ed slt = try (multiLine st ed) <|> singleLine slt
-                       <?> "raw comment"
-
-rawComments :: String -> String -> String ->
-               ParSource [StringPos]
-rawComments st ed slt =
-        E.rights <$> many1 ( try (Right <$> rawComment st ed slt) <|>
-                             try (Left  <$> (() <$ codeString))   <|>
-                                 (Left  <$> other ) )
-                -- TODO: o many? [test]
-        <?> "raw comments"
-    where
-          initP = lookAhead (try (() <$ string st))  <|> -- long comment
-                  lookAhead (try (() <$ string slt)) <|> -- line comment
-                  lookAhead (() <$ char '"')         <|> -- code string
-                  (() <$ char '\n') -- TODO: use eoft [bug]
-
-          -- non comment stuff
-          other = () <$ manyTill anyChar initP -- XXX
-
--- quoted strings, by mauke^
-codeString :: ParSource String
-codeString = q *> many ((char '\\' *> anyChar) <|> noneOf "\"\\") <* q
-             <?> "codestring"
-    where q = char '"'
-
--- single lineblock (which will be parsed) plus its original row position
-groupStringPos :: [StringPos] -> [CommentRow]
-groupStringPos sps =
-            let grp    = L.groupBy sameBlock sps'
-                -- sps'': now that they are grouped, go back to sp tuple
-                c2s qs = map (\(c,r,s,_) -> (c,r,s)) qs
-                sps''  = map c2s grp  -- this is a list of lists, hence
-                                      -- the doublemap
-            in sps2cr sps''
-    where -- how to verify if lines are sequential?
-          -- subtract [1..] to line-number and you can use Eq on this!
-
-          -- clever hack, the subtraction is the 4th value of the tuple
-          sps' = map (\((c,r,s),k) -> (c,r,s,r-k)) (zip sps [1..])
-
-          -- equality function:
-          -- multiline blocks are always to be treated alone
-          -- single line blocks are to be grouped only if on same col and
-          -- sequential lines
-          sameBlock (0,_,_,_ ) _            = False
-          sameBlock (c,_,_,il) (c',_,_,il') = c == c' && il == il'
-
-
-------------
--- SOURCE --
-------------
-
-source :: String -> String -> String -> ParSource [Issue]
-source st ed slt = fmap groupStringPos (rawComments st ed slt) >>= \cc ->
-                   fmap psPath getState                        >>= \fp ->
-                   fmap concat (mapM (parseCr fp) cc)
-    where
-          parseCr :: FilePath -> CommentRow -> ParSource [Issue]
-          parseCr fp (r,s) = case runParSource (bpp r) fp s of
-                               Right y -> pure y
-                               Left  _ -> parserFail "<source>"
-                                     -- TODO: more meaningful err mes. [duct]
-          bpp r            = getPosition                     >>= \p ->
-                             setPosition (setSourceLine p r) >>
-                             blocks
-          -- TODO: elimina runParser per runParSource [duct]
-
-
-----------
--- FILE --
-----------
-
--- main function with wich to parse issues; errors on stderr
-issueFinder :: [FilePath] -> IO [Issue]
-issueFinder fps = concat <$> mapM (uncurry fileParser) (matchPar fps)
-    where -- matches each file w/ its parser, filters parserless files
-          matchPar :: [FilePath] -> [(FilePath, ParSource [Issue])]
-          matchPar fpz = filtNotPar . map (\f -> (f, langParser f)) $ fpz
-
-          -- I can't use == on ParSource
-          filtNotPar :: [(FilePath, Maybe (ParSource [Issue]))] ->
-                        [(FilePath, ParSource [Issue])]
-          filtNotPar []                 = []
-          filtNotPar ((f, Just p):tps)  = (f,p) : filtNotPar tps
-          filtNotPar ((_, Nothing):tps) = filtNotPar tps
-
-
-fileParser :: FilePath -> ParSource [Issue] -> IO [Issue]
-fileParser fp p = rfe fp >>= \cs ->
-                  case runParSource p fp cs of
-                      Left l  -> perr (fp ++ " : parse error " ++ show l) >>
-                                 return []
-                      Right r -> return r
-    where rfe :: FilePath -> IO String
-          rfe _ = D.doesFileExist fp >>= \fb ->
-                  if fb then readFile fp
-                        else perr (fp ++ " : no such file") >> return ""
-
-          perr cs = I.hPutStrLn I.stderr cs
-
-runParSource :: ParSource a -> FilePath -> String -> Either ParseError a
-runParSource p fp cs = runParser p (ParState fp) fp cs
-
--- TODO: add more langparsers [feature:intermediate]
-langParser :: FilePath -> Maybe (ParSource [Issue])
-langParser fp | ext `elem` [".hs", ".lhs"]  = Just haskell
-              | ext `elem` [".c", ".h"]     = Just c
-              | ext `elem` [".txt"]        = Just blocks
-              | otherwise                  = Nothing
-    where ext = SF.takeExtension fp
-
-          haskell = source "{-" "-}" "--"
-          c       = source "/*" "*/" "//"
-            -- Shell: # (Perl: same)
-            -- php: as C + Shell
-            -- ruby: shell and =begin =end
-            -- javascript: C + html
-            -- python: # and """
-            -- java, objective-c, etc. watch tiobe
-            -- html
-
-         -- TODO: do not add more languages but parse every text
-         --       file? [design] [u:3]
diff --git a/src/Lentil/Parse/Issue.hs b/src/Lentil/Parse/Issue.hs
new file mode 100644
--- /dev/null
+++ b/src/Lentil/Parse/Issue.hs
@@ -0,0 +1,183 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Lentil.Parse.Issue
+-- Copyright   :  © 2015 Francesco Ariis
+-- License     :  GPLv3 (see the LICENSE file)
+--
+-- Issues parsing from comments
+-----------------------------------------------------------------------------
+
+module Lentil.Parse.Issue where
+
+import Lentil.Types
+import Lentil.Parse.Source (commentParser)
+
+import Text.Parsec
+import qualified Text.Parsec.Pos as P
+
+import Control.Applicative hiding ( (<|>), optional, many )
+
+import qualified Data.Char as C
+import qualified Data.List as L
+import qualified Data.Either as E
+import qualified System.IO as I
+
+
+-- TODO: eliminate lookahead? fai il parser davvero da bottom up senza sub
+--       parsing [lint] [refactor] ?
+-- TODO: e che fare quando il todo segue un * come nei commenti lunghi c? [design]
+
+
+-----------
+-- TYPES --
+-----------
+
+type ParIssue a = Parsec String ParState a
+
+-- file we are parsing
+data ParState = ParState { psPath    :: FilePath }
+
+
+
+---------------
+-- PRIMITIVE --
+---------------
+
+-- a unix file is defined as many lines (each one ended by '\n')
+-- this parser captures the definition and can be used as eof replacement too
+eoft :: ParIssue ()
+eoft = optional (char '\n') *> eof
+
+
+---------------
+-- PRIMITIVE --
+---------------
+
+-- TODO: add this to (ciString ancillaries?) [refactor]
+-- case insensitive string, lifted from Text.ParserCombinators.Parsec.Rfc2234
+ciString :: String -> ParIssue String
+ciString s = mapM ciChar s <?> "case insensitive string"
+    where ciChar :: Char -> ParIssue Char
+          ciChar c = char (C.toLower c) <|> char (C.toUpper c)
+
+-- i.e. remove unneeded whitespace
+htmlify :: String -> String
+htmlify cs = unwords . words $ cs
+
+-- todo add blankline to ancillaries? [lint]
+-- a blank line of text (even at eof)
+blankline :: ParIssue ()
+blankline = char '\n' *> (() <$ char '\n' <|> eof)
+-- blankline = () <$ string "\n\n"
+
+
+----------
+-- TAGS --
+----------
+
+-- simple tags parsing
+
+-- tag only
+tag :: ParIssue Tag
+tag = Tag <$> (openPar *> tagLabel <* closePar) <?> "tag"
+
+-- anything goes, apart from ' '
+tagLabel :: ParIssue String
+tagLabel = many1 (satisfy sf) <?> "tag label"
+    where sf :: Char -> Bool
+          sf c | c == closeDel = False
+               | C.isSpace c   = False
+               | otherwise     = True
+
+openPar, closePar :: ParIssue Char
+openPar  = char openDel  <?> "open-tag delimiter"
+closePar = char closeDel <?> "close-tag delimiter"
+
+
+-------------
+-- INCIPIT --
+-------------
+
+-- optional ws + flagwords (case unsensitive) + optional ':' ++ optional1 ws
+incipit :: ParIssue ()
+incipit = () <$ ( char '\n'                       *>
+                  spaces                          *>
+                  choice (map ciString flagWords) *>
+                  optional (char ':')             *>
+                  many1 (char ' ')                   )
+          <?> "incipit"
+
+-- TODO: allow tags in before description? [doc] [design]
+flagWords :: [String]
+flagWords = ["TODO", "FIXME", "XXX"]
+
+
+------------
+-- ISSUES --
+------------
+
+-- simple issue parsing
+
+-- an issue is a flagword, followed by : , followed by some description and
+-- ended optionally by some tags (No t/f? End by whiteline or eof or
+-- another TODO).
+issue :: ParIssue Issue
+issue = (Issue <$> fmap psPath getState
+               <*> (incipit *> fmap sourceLine getPosition)
+               <*> freeText
+               <*> option [] (try tags))
+        <?> "issue"
+
+
+-- any text. Since tags/fields at the end of the issue are optional, we need
+-- a way to delimit this. Delimiters are: eof, tags/fields, blank line
+freeText :: ParIssue Description
+freeText = fmap htmlify (manyTill anyChar end) <?> "free text"
+    where vp p      = try . parsecMap (const ()) $ p
+          end       = lookAhead $ choice [vp (spaces *> tag),
+                                          vp blankline,
+                                          vp incipit]
+
+    -- todo rimuovi tutti gli eof, oramai non servono [refactor]
+
+tags :: ParIssue [Tag]
+tags = many1 (try $ spaces *> tag) <?> "tags"
+
+
+
+
+-- parses a number of issues from a given line-of-text
+issues :: ParIssue [Issue]
+issues = getPosition >>= \p -> -- horrible hack, search for [duct] for expl-
+         setPosition (setSourceLine p (0)) >> -- anation
+         many (try $ manyTill anyChar (lookAhead $ try incipit) *> issue)
+         <?> "issues"
+
+
+----------
+-- FILE --
+----------
+
+-- main function with wich to parse issues; errors on stderr
+issueFinder :: [FilePath] -> IO [Issue]
+issueFinder fps = mapM commentParser fps >>= \content ->
+
+                  -- todo partial pattern! [refactor] [lint]
+                  let ass = zip fps content
+                      con' = map (\(a, Just b) -> (a,b)) .
+                             filter ((/= Nothing) . snd) $ ass in
+                  mapM pf con' >>= \iss ->
+
+                  return (concat iss)
+    where
+          pf (fp,cs) = case runParSource issues fp cs of
+                         Left l  -> perr (fp ++ " : issue parsing error " ++
+                                          show l) >> return []
+                         Right r -> return r
+
+          perr cs = I.hPutStrLn I.stderr cs
+
+-- todo deduplicate runparsource [lint] [refactor]
+runParSource :: ParIssue a -> FilePath -> String -> Either ParseError a
+runParSource p fp cs = runParser p (ParState fp) fp cs
+
diff --git a/src/Lentil/Parse/Source.hs b/src/Lentil/Parse/Source.hs
new file mode 100644
--- /dev/null
+++ b/src/Lentil/Parse/Source.hs
@@ -0,0 +1,178 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Lentil.Parse.Source
+-- Copyright   :  © 2015 Francesco Ariis
+-- License     :  GPLv3 (see the LICENSE file)
+--
+-- Comments from source files
+-----------------------------------------------------------------------------
+
+
+module Lentil.Parse.Source where
+
+import Text.Parsec hiding (Line)
+
+import qualified Data.Char as C
+import qualified System.Directory as D
+import qualified System.IO as I
+import qualified System.FilePath as SF
+
+import Control.Applicative hiding (many, (<|>))
+
+-----------
+-- TYPES --
+-----------
+
+-- our parsers goal is simple: from a source file, only the comments and
+-- newlines
+type ParSource a = Parsec String () a
+
+data ParSyntax = ParSyntax { psLineComms  :: [String],
+                             psBlockComms :: [(String, String)],
+                             psStringLits :: [Char],
+                             psCharLits   :: [Char] }
+                 deriving (Show)
+
+
+------------
+-- BLOCKS --
+------------
+
+-- TODO: ++ "\n" is inefficient [lint] [refactor]
+lineComment :: String -> ParSource String
+lineComment s = string s *> fmap (++ "\n") (manyTill anyChar newline)
+                <?> "line comment"
+
+blockComment :: (String, String) -> ParSource String
+blockComment (i, e) = string i *> manyTill anyChar (try $ string e)
+                      <?> "block comment"
+
+-- quoted strings, escaped by \, by mauke^
+litString :: Char -> ParSource String
+litString ic = q *> many ((char '\\' *> anyChar) <|> noneOf no) <* q
+               <?> "codestring"
+    where q  = char ic
+          no = C.showLitChar ic "\\"
+
+-- quoted single character
+litChar :: Char -> ParSource Char
+litChar ic = q *> ((char '\\' *> anyChar) <|> anyChar) <* q
+             <?> "char string sign"
+    where q = char ic
+    -- TODO: fallo diventare string così non impazzisci nelle altre parti
+    --       [refactor]
+
+-- a program is instructions to the computer. Ends when you meet
+-- a well formed element from above (linecomm, blockcom, stringlit,
+-- charLit)
+program :: ParSyntax -> ParSource String
+program ps = manyTill1 anyChar (endp <|> ("" <$ eof))
+             <?> "program"
+    where
+          -- endp :: [ParSource String]
+          endp  = lookAhead $ choice $ map try $ map string posts ++ [lchars]
+
+          -- every unambiguous symbol (init comments, listring char)
+          posts  = psLineComms ps ++ map fst (psBlockComms ps) ++
+                   (map (:[]) $ psStringLits ps)
+
+          -- ambiguous symbols (pall'aaa'foo is valid haskell identifier,
+          -- so it should stay here
+          lchars = "" <$ choice (map litChar (psCharLits ps))
+
+          -- TODO: lint program parser [lint]
+
+
+-- occhio che il primo carattere non è controllato da ED
+-- TODO: manyTill1 broken? occhio che al primon on chiama ed! [test]
+manyTill1 :: ParSource a -> ParSource b -> ParSource [a]
+manyTill1 p ed = (:) <$> p <*> manyTill p ed
+
+
+------------
+-- SOURCE --
+------------
+
+-- given a set of lineparsers / blockparsers
+source :: ParSyntax -> ParSource String
+source ps@(ParSyntax lc bc sl cl) =
+                choice (map (try . lineComment) lc) -- line comment
+            <|> choice (map (try . blockComment) bc) -- block comment
+            <|> (choice (map (onlynl . try . litString) sl))
+            <|> ("" <$ (choice (map (try . litChar) cl))) -- todo ugly parens [lint] [refactor]
+            <|> (onlynl $ program ps)
+            <?> "source file"
+    where onlynl a = fmap (filter (== '\n')) a
+
+sources :: ParSyntax -> ParSource String
+sources ps = fmap concat $ many1 (source ps)
+
+
+--------------
+-- SYNTAXES --
+--------------
+
+-- TODO: add more langparsers [feature:intermediate]
+-- TODO: fallo che sia estensibile e leggibile
+--       a compilazione [design] [u:3]
+
+langParser :: String -> Maybe (ParSource String)
+langParser fp | ext `elem` [".hs", ".lhs"]         = Just haskell
+              | ext `elem` [".c", ".h"]            = Just c
+              | ext `elem` [".cpp", ".hpp"]        = Just c -- C++
+              | ext `elem` [".java"]               = Just c -- Java
+              | ext `elem` [".js"]                 = Just javascript
+              | ext `elem` [".py"]                 = Just python
+              | ext `elem` [".rb"]                 = Just ruby
+              | ext `elem` [".pas", ".pp", ".inc"] = Just pascal
+              | ext `elem` [".txt"]                = Just text
+              | otherwise                          = Nothing
+    where ext = SF.takeExtension fp
+
+haskell, c, pascal, text :: ParSource String
+haskell    = sources $ ParSyntax ["--"] [("{-", "-}")] ['"']  ['\'']
+c          = sources $ ParSyntax ["//"] [("/*", "*/")] ['"']  ['\'']
+javascript = sources $ ParSyntax ["//"] [("/*", "*/")] ['"', '\''] []
+pascal     = sources $ ParSyntax ["//"] [("{",  "}" ),
+                                         ("(*", "*)")] ['\''] []
+python     = sources $ ParSyntax ["#"]  [("\"\"\"",
+                                          "\"\"\"")]   ['"', '\''] []
+ruby       = sources $ ParSyntax ["#"] [("=begin",
+                                         "=end")]      ['"', '\''] []
+text       = many anyChar
+
+
+-------------
+-- PARSERS --
+-------------
+
+
+commentParser :: FilePath -> IO (Maybe String)
+commentParser fp = case langParser fp of
+                     Nothing -> return Nothing
+                     Just p  -> fileParser fp p >>=
+                                return . Just . ('\n':)
+                                -- TODO: ugly hack to allow tODOs to be
+                                -- parseable even in first line. This is
+                                -- coupled with another hack in parser
+                                -- `issue` (another module). [refactor]
+                                -- [lint] [duct]
+
+
+-- errors to stderr
+fileParser :: FilePath -> ParSource String -> IO String
+fileParser fp p = rfe fp >>= \cs ->
+                  case runParSource p fp cs of
+                      Left l  -> perr (fp ++ " : parse error " ++ show l) >>
+                                 return []
+                      Right r -> return r
+    where rfe :: FilePath -> IO String
+          rfe _ = D.doesFileExist fp >>= \fb ->
+                  if fb then readFile fp
+                        else perr (fp ++ " : no such file") >> return ""
+
+          perr cs = I.hPutStrLn I.stderr cs
+
+runParSource :: ParSource a -> FilePath -> String -> Either ParseError a
+runParSource p fp cs = runParser p () fp cs
+
diff --git a/src/Lentil/Print.hs b/src/Lentil/Print.hs
--- a/src/Lentil/Print.hs
+++ b/src/Lentil/Print.hs
@@ -45,7 +45,7 @@
 ppTag c t = col (char openDel) <> string (tagString t) <>
             col (char closeDel)
     where col | c == Red  = red
-              | c == Blue = blue
+              | otherwise = blue
 
 ppIssue :: Int -> Issue -> Doc
 ppIssue ind is = indent spInd ( fillSep [string iNum, string "",
@@ -63,7 +63,6 @@
 ppFile ind is = text (drop 2 $ iFile (head is)) PP.<$>
                 vsep (map (ppIssue ind) is)
 
--- TODO: choose report width (test senza tag)
 ppIssues :: Bool -> [Issue] -> String
 ppIssues col is = myRender col $ vsep (L.intersperse softline igr)
     where
diff --git a/test/Lentil/ArgsSpec.hs b/test/Lentil/ArgsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/ArgsSpec.hs
@@ -0,0 +1,40 @@
+module Lentil.ArgsSpec where
+
+import Test.Hspec
+
+import Lentil.Types
+import Lentil.Query
+import Lentil.Args
+
+
+-- TEST VARS --
+
+-- TODO: can't test lOpts, need to make a runner and a validator on
+--       the output from io [test]
+
+testOpt :: LOptions
+testOpt = LOptions (["alpha"], ["beta"]) Csv
+                   [filterFilepath "al", filterTagless]
+                   [] (Just "foo.txt")
+
+ins :: [String]
+ins  = words "alpha --format csv -x beta -p al -t ^ --output foo.txt"
+
+-- out :: Maybe LOptions
+-- out = getParseResult .
+      -- execParserPure M.mempty (info lOpts (fullDesc)) $ ins
+
+
+-- TESTING --
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+
+  describe "lOpts" $ do
+    it "parses options" $
+      ins `shouldBe` ins
+      --error "args" --out `shouldBe` Just testOpt
+
diff --git a/test/Lentil/ExportSpec.hs b/test/Lentil/ExportSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/ExportSpec.hs
@@ -0,0 +1,29 @@
+module Lentil.ExportSpec where
+
+
+import Test.Hspec
+
+import Lentil.Types
+import Lentil.Export
+
+
+is :: [Issue]
+is = [Issue "file" 1 "da" [Tag "a", Tag "c"],
+      Issue "file" 2 "db" [Tag "e:f"]]
+
+csv :: String
+csv = "\"Filepath\",\"Row\",\"Description\",\"Tags\"\n\
+      \\"file\",\"1\",\"da\",\"a c\"\n\
+      \\"file\",\"2\",\"db\",\"e:f\""
+
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+
+  describe "issues2CSV" $ do
+    it "exports issues to CSV" $
+      issues2CSV is `shouldBe` csv
+
diff --git a/test/Lentil/FileSpec.hs b/test/Lentil/FileSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/FileSpec.hs
@@ -0,0 +1,46 @@
+module Lentil.FileSpec where
+
+import Test.Hspec
+
+import Lentil.File
+
+-- File tests
+
+main :: IO ()
+main = hspec spec
+
+bas :: String
+bas = "test/test-files/test-proj/"
+
+spec :: Spec
+spec = do
+
+  describe "findIssues" $ do
+    it "scans directories to find issues" $
+      findIssues [bas ++ ""] [] >>= \fiss ->
+      length fiss `shouldBe` 5
+    it "allows to add specific folders" $
+      findIssues [bas ++ ""] [] >>= \fiss ->
+      findIssues [bas ++ "fold-a/", bas ++ "fold-b",
+                  bas ++ "fold-c"] [] >>= \fiss2 ->
+      length fiss `shouldBe` length fiss2
+    it "accepts filenames instead of dirs, too" $
+      findIssues [bas ++ "fold-a/foo1.hs"] [] >>= \fiss ->
+      length fiss `shouldBe` 1
+    it "doesn't duplicate issues" $
+      findIssues [bas ++ "", bas ++ ""] [] >>= \fiss ->
+      length fiss `shouldBe` 5
+    it "excludes some folders" $
+      findIssues [bas ++ ""] [bas ++ "fold-c/"] >>= \fiss ->
+      length fiss `shouldBe` 2
+    it "excludes files" $
+      findIssues [bas ++ ""] [bas ++ "fold-a/foo1.hs"] >>= \fiss ->
+      length fiss `shouldBe` 4
+    it "excludes subfolders" $
+      findIssues [bas ++ "fold-c/"]
+                 [bas ++ "fold-c/sub-fold/"] >>= \fiss ->
+      length fiss `shouldBe` 1
+    it "doesn't crash on wrong file" $
+      findIssues [bas ++ "zxczd"] [] >>= \fiss ->
+      length fiss `shouldBe` 0
+
diff --git a/test/Lentil/Parse/IssueSpec.hs b/test/Lentil/Parse/IssueSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/Parse/IssueSpec.hs
@@ -0,0 +1,137 @@
+module Lentil.Parse.IssueSpec where
+
+import Test.Hspec
+import Text.Parsec ( runParser )
+import Text.Parsec.Char
+import Text.Parsec.Combinator
+import Control.Applicative
+
+import Lentil.Types
+import Lentil.Parse.Issue
+
+-- Parsing tests
+
+-- simple parser (choosable if we are at begin of line or else)
+sp :: ParIssue a -> String -> Maybe a
+sp p cs = either (const Nothing) Just
+                    (runParser p (ParState fp) fp cs)
+    where fp = "<f>"
+
+
+main :: IO ()
+main = hspec spec
+
+
+
+spec :: Spec
+spec = do
+
+  describe "ciString" $ do
+    it "parses a case insensitive string" $
+      sp (ciString "tEst") "Test" `shouldBe` Just "Test"
+
+  describe "eoft" $ do
+    it "parses a textfile end of file" $
+      sp (string "test" <* eoft) "test\n" `shouldBe` Just "test"
+    it "or an eof without '\\n'" $
+      sp (string "test" <* eoft) "test"   `shouldBe` Just "test"
+
+  describe "blankline" $ do
+    it "parses an empty line" $
+      sp blankline "\n\nlol"   `shouldBe` Just ()
+    it "deals with eof blanklines too" $
+      sp blankline "\n" `shouldBe` Just ()
+
+  describe "htmlify" $ do
+    it "removes unneeded whitespace" $
+      htmlify "one  two " `shouldBe` "one two"
+    it "removes newlines too" $
+      htmlify "one  two\nthree " `shouldBe` "one two three"
+
+  describe "tag" $ do
+    it "parses a tag" $
+      fmap tagString (sp tag "[test-tag]") `shouldBe` Just "test-tag"
+    it "should not allow whitespace inside a tag" $
+      fmap tagString (sp tag "[broken tag]") `shouldBe` Nothing
+
+  describe "incipit" $ do
+    it "parses the initial section of an issue, returns nothing" $
+       do sp incipit "\nTODO: "  `shouldBe` Just ()
+          sp incipit "\nFIXME: "  `shouldBe` Just ()
+          sp incipit "\nXXX: "  `shouldBe` Just ()
+    it "doesn't work without previous newline" $
+      sp incipit "\nTODO:some"  `shouldBe` Nothing
+    it "doesn't work if you don't add a space after :" $
+      sp incipit "\nTODO:some"  `shouldBe` Nothing
+    it "does allow you to omit the :" $
+      sp incipit "\nxxx some"  `shouldBe` Just ()
+    it "is case unsensitive in flag-word" $
+      sp incipit "\ntOdO some"  `shouldBe` Just ()
+    it "allows whitespace before the todo" $
+      sp incipit "\n   fixme some"  `shouldBe` Just ()
+    it "doesn't allow anything else before flag-words" $
+      sp incipit "\nso fixme someday"  `shouldBe` Nothing
+    it "fails if we are not at the beginning of line" $
+      sp incipit "\nso fixme someday"  `shouldBe` Nothing
+
+  describe "tags" $ do
+    it "parses a series of tags, sep by whitespace" $
+      sp tags " [tag] [field:test] [tog]"
+         `shouldBe` Just [Tag "tag", Tag "field:test", Tag "tog"]
+    it "allows ending whitespace" $
+      sp tags "   [tag] [field:test] [tog] "
+         `shouldBe` Just [Tag "tag", Tag "field:test", Tag "tog"]
+
+  describe "freeText" $ do
+    it "parses a free form text" $
+      sp freeText "this is it\n\n" `shouldBe` Just "this is it"
+    it "can be ended by tags/fields" $
+      sp freeText "this is it [field:val]" `shouldBe` Just "this is it"
+    it "trims extra whitespace" $
+      sp freeText "this is  it [bug]" `shouldBe` Just "this is it"
+    it "cannot be ended by new issue on same line" $
+      sp freeText "this is it   TODO: desc\n" `shouldBe`
+             Just "this is it TODO: desc"
+    it "cannot be ended by new issue on new-but-not-beginning-of-line" $
+      sp freeText "this is it \nn  TODO: desc\n" `shouldBe`
+             Just "this is it n TODO: desc"
+    it "can be ended by blank line" $
+      sp freeText "this is it\n\n  TODO: desc" `shouldBe` Just "this is it"
+    it "can be ended by incomplete blank line (eof)" $
+      sp freeText "this is it\n" `shouldBe` Just "this is it"
+
+  describe "issue" $ do
+    it "parses an issue" $
+      sp issue "\n fixMe this is it [t] [f:w]"
+         `shouldBe` (Just $ Issue "<f>" 2 "this is it" [Tag "t", Tag "f:w"])
+    it "fails if were are not at beginning of a line" $
+      sp issue "fixMe this is it [t] [f:w]"
+         `shouldBe` Nothing
+    it "parses tagless/fieldless issues too" $
+      sp issue "\nTODO: this is it\n"
+         `shouldBe` (Just $ Issue "<f>" 2 "this is it" [])
+    it "doesn't display the eventual \n at eof" $
+      sp issue "\nTOdO: this is it\n"
+         `shouldBe` (Just $ Issue "<f>" 2 "this is it" [])
+    it "doesn't accept a lone (naked) todo" $
+      sp issue "\ntodo\n"
+         `shouldBe` Nothing
+
+  describe "issues" $ do
+    it "parses multiple issues" $
+      sp issues "\nTODO: this is it [f:w]\n TODO: hey [t]"
+         `shouldBe` Just [Issue "<f>" 1 "this is it" [Tag "f:w"],
+                          Issue "<f>" 2 "hey" [Tag "t"]]
+    it "don't parse multiple issues if on the same line" $
+      sp issues "\nTODO: this is it [f:w] TODO: hey [t]"
+         `shouldBe` Just [Issue "<f>" 1 "this is it" [Tag "f:w"]]
+
+  describe "issueFinder" $ do
+    it "reads a file (code or txt) for issues" $
+      let fp = "test/test-files/lang-comm/test.txt" in
+      issueFinder [fp] >>= \i ->
+      i `shouldBe` [Issue fp 1 "palla" [],
+                    Issue fp 3 "beta"  [],
+                    Issue fp 4 "gamma" [],
+                    Issue fp 6 "qqq"   [Tag "bug"]]
+
diff --git a/test/Lentil/Parse/SourceSpec.hs b/test/Lentil/Parse/SourceSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/Parse/SourceSpec.hs
@@ -0,0 +1,112 @@
+module Lentil.Parse.SourceSpec where
+
+import Test.Hspec
+import Text.Parsec (runParser)
+import Text.Parsec.Char
+import Text.Parsec.Combinator
+import Control.Applicative
+
+import Lentil.Parse.Source
+
+-- Parsing tests
+
+-- simple parser
+sp :: ParSource a -> String -> Maybe a
+sp p cs = either (const Nothing) Just
+                 (runParser p () fp cs)
+    where fp = "<f>"
+
+
+main :: IO ()
+main = hspec spec
+
+
+spec :: Spec
+spec = do
+
+  describe "manyTill1" $ do
+    it "behaves like manyTill on non-empty string" $
+      sp (manyTill1 anyChar newline) "foo\n" `shouldBe`
+      sp (manyTill anyChar newline) "foo\n"
+    it "doesn't runtime crash on a empty string (instead returns [])" $
+      sp (manyTill1 anyChar newline) "" `shouldBe` Just []
+
+  describe "lineComment" $ do
+    it "parses a line comment" $
+      sp (lineComment "//") "// Test\n" `shouldBe` Just " Test\n"
+    it "fails without trailing \\n" $
+      sp (lineComment "//") "// Test"   `shouldBe` Nothing
+
+  describe "blockComment" $ do
+    it "parses a block comment" $
+      sp (blockComment ("/*","*/")) "/* Test\n2 */" `shouldBe`
+         Just " Test\n2 "
+
+  describe "litString" $ do
+    it "parses code string" $
+      sp (litString '"') "\"palla\"" `shouldBe` Just "palla"
+    it "parses code string with escaped \" inside" $
+      sp (litString '"') "\"pal\\\"la\"" `shouldBe` Just "pal\"la"
+    it "parses code string with comments symbols inside" $
+      sp (litString '"') "\"pal#la\"" `shouldBe` Just "pal#la"
+
+  describe "litChar" $ do
+    it "parses a string literal character inside" $
+      sp (litChar '\'') "'a'" `shouldBe`  Just 'a'
+    it "parses escaped characters too" $
+      sp (litChar '\'') "'\"'" `shouldBe` Just '\"'
+
+  let hss = ParSyntax ["--"] [("{-", "-}")] ['"']  ['\'']
+  describe "program" $ do
+    it "parses program instructions till eof" $
+      sp (program hss) "prova " `shouldBe`  Just "prova "
+    it "stops at single-line comment" $
+      sp (program hss) "prova -- " `shouldBe`  Just "prova "
+    it "stops at blockcomment" $
+      sp (program hss) "prova {- " `shouldBe`  Just "prova "
+    it "stops at literal string" $
+      sp (program hss) "prova \"babby " `shouldBe`  Just "prova "
+    it "stops at literal char" $
+      sp (program hss) "prova ' '" `shouldBe`  Just "prova "
+    it "stops at ' which is not a literal char" $
+      sp (program hss) "prova' " `shouldBe`  Just "prova' "
+
+  let rbs = ParSyntax ["#"] [] ['"', '\'']  []
+  describe "source" $ do
+    it "parses one piece of source (line-comment)" $
+      sp (source hss) "-- hey\n my " `shouldBe`  Just " hey\n"
+    it "parses one piece of source (block-comment)" $
+      sp (source hss) "{-hey-}\n my " `shouldBe`  Just "hey"
+    it "parses one piece of source (string-literal)" $
+      sp (source hss) "\"hey\"" `shouldBe`  Just ""
+    it "parses a string for language with ' and \" available" $
+      sp (source rbs) "\"he#y\"" `shouldBe`  Just ""
+    it "parses one piece of source (char-literal)" $
+      sp (source hss) "\'h\'" `shouldBe`  Just ""
+    it "parses one piece of source (program instructions)" $
+      sp (source hss) "prime'" `shouldBe`  Just ""
+
+  describe "commentParser" $ do
+    beforeAll (readFile "test/test-files/lang-comm/out.blocks") $ do
+      it "parses a haskell source" (\a ->
+        commentParser "test/test-files/lang-comm/haskell.hs" >>= \s ->
+        s `shouldBe` Just a)
+      it "parses a C source" (\a ->
+        commentParser "test/test-files/lang-comm/clang.c" >>= \s ->
+        s `shouldBe` Just a)
+      it "parses a Pascal source" (\a ->
+        commentParser "test/test-files/lang-comm/pascal.pas" >>= \s ->
+        s `shouldBe` Just a)
+      it "parses a plain text source" (\a ->
+        commentParser "test/test-files/lang-comm/text.txt" >>= \s ->
+        s `shouldBe` Just a)
+      it "parses a javascript source" (\a ->
+        commentParser "test/test-files/lang-comm/javascript.js" >>= \s ->
+        s `shouldBe` Just a)
+      it "parses a python source" (\a ->
+        commentParser "test/test-files/lang-comm/python.py" >>= \s ->
+        s `shouldBe` Just a)
+      it "parses a ruby source" (\a ->
+        commentParser "test/test-files/lang-comm/ruby.rb" >>= \s ->
+        s `shouldBe` Just a)
+
diff --git a/test/Lentil/PrintSpec.hs b/test/Lentil/PrintSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/PrintSpec.hs
@@ -0,0 +1,20 @@
+module Lentil.PrintSpec where
+
+import Test.Hspec
+
+-- import Lentil.Types
+import Lentil.Print
+
+-- Parsing tests
+
+main :: IO ()
+main = hspec spec
+
+
+spec :: Spec
+spec = do
+
+
+  describe "alignNumber" $ do
+    it "right aligns an integer" $
+      alignNumber  4 30 `shouldBe` "  30"
diff --git a/test/Lentil/QuerySpec.hs b/test/Lentil/QuerySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Lentil/QuerySpec.hs
@@ -0,0 +1,80 @@
+module Lentil.QuerySpec where
+
+import Test.Hspec
+
+import Lentil.Types
+import Lentil.Query
+
+-- Parsing tests
+
+main :: IO ()
+main = hspec spec
+
+
+spec :: Spec
+spec = do
+
+  let i1 = Issue "a.hs" 1 "issue 1"  [Tag "ta", Tag "fl:va"]
+      i2 = Issue "a.hs" 2 "issue 12" [Tag "tb"]
+      i3 = Issue "b.hs" 1 "issue 3"  [Tag "tb", Tag "fl:vb"]
+      ix = Issue "b.hs" 1 "issue n"  []
+      is = [i1, i2, i3]
+
+  describe "filter*" $ do
+    it "filters by Filepath" $
+      filterFilepath "a.hs" is `shouldBe` [i1, i2]
+    it "filters by Description" $
+      filterDescription "3" is `shouldBe` [i3]
+    it "filters by Tag" $
+      filterTags "tb" is `shouldBe` [i2, i3]
+    it "filters by Tag 'label'" $
+      filterTags "fl:" is `shouldBe` [i1, i3]
+    it "filters by using regexps" $
+      filterTags "(ta|tb)" is `shouldBe` [i1, i2, i3]
+
+  describe "filterTagless" $ do
+    it "filters by empty values" $
+      filterTagless (ix:is) `shouldBe` [ix]
+
+  describe "negFilter" $ do
+    it "negates a filter" $
+      negFilter (filterTags "fl:") is `shouldBe` [i2]
+    it "cancels itself on a double negation" $
+      negFilter (negFilter (filterTags "fl:")) is
+                `shouldBe` filterTags "fl:" is
+
+  describe "filter(And|Or)" $ do
+    it "chains (boolean and) multiple filters" $
+      filterAnd [filterFilepath "a.hs", filterTags "b"] is
+                `shouldBe` [i2]
+    it "chains (boolean or) multiple filters" $
+      filterOr [filterFilepath "a.hs", filterTags "b"] is
+                `shouldBe` [i1, i2, i3]
+
+  describe "sortIssues" $ do
+    it "sorts Issues" $
+      sortIssues iFile Asc is `shouldBe` [i1, i2, i3]
+    it "sorts Issues in descending order" $
+      sortIssues iFile Desc is `shouldBe` [i3, i2, i1]
+    it "smart sorts strings containing naturals" $
+      sortIssues iDesc Asc is `shouldBe` [i1, i3, i2]
+
+  describe "sortTag" $ do
+    it "sorts by partial tag, empty fields first" $
+      sortTag "fl:" Asc is `shouldBe` [i2, i1, i3]
+
+  describe "chainSorts" $ do
+    it "sequences sort functions" $
+      chainSorts is [sortIssues iFile Desc,
+                     sortIssues iDesc Desc]
+                 `shouldBe` [i3, i2, i1]
+
+  describe "groupIssues" $ do
+    it "group isses" $
+      groupIssues iFile is `shouldBe` [[i1, i2], [i3]]
+
+  describe "tagPop" $ do
+    it "outputs tag popularity list" $
+      tagPop is `shouldBe` [(Tag "tb", 2), (Tag "ta", 1),
+                            (Tag "fl:vb", 1), (Tag "fl:va", 1)]
+
diff --git a/test/test-files/lang-comm/clang.c b/test/test-files/lang-comm/clang.c
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/clang.c
@@ -0,0 +1,11 @@
+// single
+// comment
+
+var c = "st/*ring"; // string
+
+/* block
+   comment */
+
+char letter = 'c' // char
+
+//  end
diff --git a/test/test-files/lang-comm/haskell.hs b/test/test-files/lang-comm/haskell.hs
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/haskell.hs
@@ -0,0 +1,11 @@
+-- single
+-- comment
+
+someOther = "  \"  " -- string
+
+{- block
+   comment -}
+
+char' = '\n' -- char
+
+--  end
diff --git a/test/test-files/lang-comm/javascript.js b/test/test-files/lang-comm/javascript.js
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/javascript.js
@@ -0,0 +1,11 @@
+// single
+// comment
+
+var c = "st/*ring"; // string
+
+/* block
+   comment */
+
+var letter = 'c' // char
+
+//  end
diff --git a/test/test-files/lang-comm/out.blocks b/test/test-files/lang-comm/out.blocks
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/out.blocks
@@ -0,0 +1,12 @@
+
+ single
+ comment
+
+ string
+
+ block
+   comment 
+
+ char
+
+  end
diff --git a/test/test-files/lang-comm/pascal.pas b/test/test-files/lang-comm/pascal.pas
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/pascal.pas
@@ -0,0 +1,11 @@
+// single
+// comment
+
+qqq := 'Afla' // string
+
+{ block}
+(*   comment *)
+
+c := '"' // char
+
+//  end
diff --git a/test/test-files/lang-comm/python.py b/test/test-files/lang-comm/python.py
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/python.py
@@ -0,0 +1,11 @@
+# single
+# comment
+
+c = "st/*ring" # string
+
+""" block
+   comment """
+
+letter = 'c' # char
+
+#  end
diff --git a/test/test-files/lang-comm/ruby.rb b/test/test-files/lang-comm/ruby.rb
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/ruby.rb
@@ -0,0 +1,11 @@
+# single
+# comment
+
+puts "str#ing" # string
+
+=begin block
+   comment =end
+
+a = [1, 'hi', 3.14, 1, 2, [4, 5]] # char
+
+#  end
diff --git a/test/test-files/lang-comm/test.txt b/test/test-files/lang-comm/test.txt
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/test.txt
@@ -0,0 +1,9 @@
+TODO: palla
+
+fixme beta
+xxx gamma
+
+fixme qqq [bug]
+gaeta
+    
+
diff --git a/test/test-files/lang-comm/text.txt b/test/test-files/lang-comm/text.txt
new file mode 100644
--- /dev/null
+++ b/test/test-files/lang-comm/text.txt
@@ -0,0 +1,11 @@
+ single
+ comment
+
+ string
+
+ block
+   comment 
+
+ char
+
+  end
diff --git a/test/test-files/test-proj/fold-a/foo1.hs b/test/test-files/test-proj/fold-a/foo1.hs
new file mode 100644
--- /dev/null
+++ b/test/test-files/test-proj/fold-a/foo1.hs
@@ -0,0 +1,1 @@
+fac' c = product [1..c] -- TODO: sometodo [t]
diff --git a/test/test-files/test-proj/fold-b/foo2.hs b/test/test-files/test-proj/fold-b/foo2.hs
new file mode 100644
--- /dev/null
+++ b/test/test-files/test-proj/fold-b/foo2.hs
@@ -0,0 +1,1 @@
+-- TODO: sometodo [t]
diff --git a/test/test-files/test-proj/fold-c/foo3.hs b/test/test-files/test-proj/fold-c/foo3.hs
new file mode 100644
--- /dev/null
+++ b/test/test-files/test-proj/fold-c/foo3.hs
@@ -0,0 +1,1 @@
+-- TODO: sometodo [t]
diff --git a/test/test-files/test-proj/fold-c/sub-fold/foo4.hs b/test/test-files/test-proj/fold-c/sub-fold/foo4.hs
new file mode 100644
--- /dev/null
+++ b/test/test-files/test-proj/fold-c/sub-fold/foo4.hs
@@ -0,0 +1,1 @@
+-- TODO: sometodo [t]
diff --git a/test/test-files/test-proj/fold-c/sub-fold/foo5.hs b/test/test-files/test-proj/fold-c/sub-fold/foo5.hs
new file mode 100644
--- /dev/null
+++ b/test/test-files/test-proj/fold-c/sub-fold/foo5.hs
@@ -0,0 +1,1 @@
+-- TODO: sometodo [t]
