packages feed

lentil 0.1.1.3 → 0.1.2.0

raw patch · 26 files changed

+583/−334 lines, 26 filesdep ~base

Dependency ranges changed: base

Files

changes.txt view
@@ -1,8 +1,18 @@+0.1.2.0+-------++- Released Wed 24 Jun 2015 03:46:07 CEST+- added ``-f compiler`` option to make output emacs parseable+  (requested by Henning Thielemann).+- fixed contiguous single-line/block-comment bug (reported by+  Henning Thielemann).+ 0.1.1.3 -------  - Released Mon 15 Jun 2015 23:29:49 CEST-- fixmes and xxx issues now get a free [fixme]/[xxx] tag+- fixmes and xxx issues now get a free [fixme]/[xxx] tag (requested+  by qptain nemo).  0.1.1.2 -------
doc/usr/page.rst view
@@ -73,7 +73,7 @@    lentil . -This should output a basic list of issues.+This should output a basic list of issues:  :: @@ -190,14 +190,18 @@    <p><br/></p>  -Output options+Format options -------------- -You can change the ``-f TYPE`` option to change the output format. You-can choose the default format ``pretty``, export issues to CSV format-using ``-f csv`` or pick ``tagpop``. Tagpop lists tags by their popularity-(reverse order) and is useful to see a summary of the open issues:+The ``-f TYPE`` option modifies the output format, with ``pretty`` being+the default. +``-f csv`` export issues to CSV (RFC 4180).+++``-f tagpop``, lists tags by their popularity (reverse order),+useful to get a summary of open issues:+ ::    lentil . -x test/test-files/ -f tagpop@@ -212,9 +216,20 @@        1  [urgency:3]        1  [design] +``-f comp`` displays issues in a format similar to the one emitted+by compilers (GCC, GHC, etc.): -``--output VERSION`` is used to output the report to a file instead of stdout.+:: +  alpha.hs:4:+     add prompt before getline, or the user might complain! [interface]++  alpha.hs:11:+     add a type signature to replace [lint]++This is useful because it is recognised by editors like EMACS,+which can turn them into active links to the relevant file/position.+ .. raw:: html     <p><br/></p>@@ -223,6 +238,10 @@ Other options ------------- -``-v`` outputs the program version, ``--help`` displays cheatsheet option-help.+``--output FILE`` is used to output the report to a file instead of stdout.++``-v`` outputs the program version.++``--help`` displays cheatsheet option help.+ 
issues.txt view
@@ -1,3 +1,9 @@+changes.txt+    14  issues now get a free [xxx] [fixme]+    33  something <-- is valid now) - more languages available (c++, java,+        javascript, python, ruby) - fixed end-of-file bug and double quote+        string literal+  doc/dev/issues.txt     10  from String to Text? [feature:advanced]     11  colorise output on regexps? [feature:advanced]@@ -9,64 +15,68 @@     17  symlinks? [feature:intermediate]     18  check --output pretty, there are trailing white spaces [bug] [u:1]     19  test con 7.6.3 [feature:basic] [test]-    20  todo/fixmes in different sections [request] [u:3]-    21  linux kernel, lentil si inceppa su dei caratteri [bug]-    22  da cabal a hpack [u:1] [feature:advanced]-    23  ad oggi veramente lento [u:2] [refactor]-    24  list 'ok' tags somehow? put them in doc? [doc] [design]-    25  sm: issue numbers is valuable [design] [request]-    26  sm: have a way to link to discussion/history of issue [design]+    20  linux kernel, lentil si inceppa su dei caratteri [bug]+    21  da cabal a hpack [u:1] [feature:advanced]+    22  ad oggi veramente lento [u:2] [refactor]+    23  sm: issue numbers is valuable [design] [request]+    24  sm: have a way to link to discussion/history of issue [design]         [request]-    27  sm: 1. lentil autogenerates numbers where missing adding tags, 2.+    25  sm: 1. lentil autogenerates numbers where missing adding tags, 2.         creates an associated numbered file in .issues/, displays         associated notes written in those files is asked, 3. even update         them when there is a source change. (4) these files could be         webserved when they want something linkable. Vedi comments in         doc/dev/issues.txt [design] [request]-    33  chickpea, lentil's brother. Two commands, add number to numberless+    31  chickpea, lentil's brother. Two commands, add number to numberless         tag (#1212 or something), create a mini db with those issues, which         automatically updated and you can even write stuff in there. O         maybe you can even add lentil, chickpea and quinoa. Vedi more in         doc/dev/issues.txt [design]+    37  make it lazy in ouputting todos [refactor]+    38  way to 'group' (section) output by tag [feature:advanced]  +doc/dev/sm.txt+    13  issue from something like+    15  take the dog out for a walk [refactor]+    19  #301 take the dog out for a walk [refactor]+    25  referring to this, and another part editable freely.+  src/Lentil/Args.hs     23  disambiguation optparse-applicative [feature:intermediate]     24  help as lentil PATH [ PATH... ] [ OPTIONS ] [feature:intermediate]     89  uncomment and implement sorting [feature:intermediate]+   100  sort parsing? [feature:intermediate]   src/Lentil/File.hs-    49  combine funziona su windows? [feature:intermediate]+    51  combine funziona su windows? [feature:intermediate]   src/Lentil/Parse/Issue.hs-    26  eliminate lookahead? fai il parser davvero da bottom up senza sub-        parsing [lint] [refactor]-    28  e che fare quando il todo segue un * come nei commenti lunghi c?+    23  e che fare quando il todo segue un * come nei commenti lunghi c?         [design]-    56  add this to (ciString ancillaries?) [refactor]-    67  add blankline to ancillaries? [lint]-   110  allow tags in before description? [doc] [design]-   141  rimuovi tutti gli eof, oramai non servono [refactor]-   165  partial pattern! [refactor] [lint]-   180  deduplicate runparsource [lint] [refactor]+    63  add this to (ciString ancillaries?) [refactor]+   111  usa spaces non ' ' [fixme]+   154  specifica meglio [fixme]  -src/Lentil/Parse/Source.hs-    41  ++ "\n" is inefficient [lint] [refactor]-    62  fallo diventare string così non impazzisci nelle altre parti-        [refactor]-    83  lint program parser [lint]-    87  manyTill1 broken? occhio che al primon on chiama ed! [test]-   102  ugly parens [lint] [refactor]-   116  add langparsers che sia estensibile e leggibile a compilazione-        [u:3]-   155  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]+src/Lentil/Parse/Run.hs+    31  a function String -> [Issue]  -src/Lentil/Print.hs-    43  colours are bad for unix philosophy, remove them? hlint manages to-        get a colorised output when ran alone but not when piped to less,-        how? [design]+src/Lentil/Parse/Syntaxes.hs+    19  add langparsers che sia estensibile e leggibile a compilazione+        [u:2] [feature:intermediate]+    35  multiline signature? [lint]+    36  tag at the beginning too? [design]  +src/Main.hs+    35  add doc to use less -r when piping hIsTerminalDevice stdout [doc]+  test/Lentil/ArgsSpec.hs     12  can't test lOpts, need to make a runner and a validator on the         output from io [test]+ +test/Tests.hs+     3 +   http://hackage.haskell.org/package/nanospec-0.2.1/docs/Test-Hspec.html+   use this? [refactor]+     6 +   http://hackage.haskell.org/package/mockery-0.3.0/docs/Test-Mockery-Directory.html+   instead of folder use this? [refactor]
lentil.cabal view
@@ -1,5 +1,5 @@ name:                lentil-version:             0.1.1.3+version:             0.1.2.0 synopsis:            frugal issue tracker description:         minumum effort, cohesive issue tracker based on                      ubiquitous @TODO@s and @FIXME@s conventions.@@ -41,7 +41,8 @@                        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.Source,-                       Lentil.Parse.Issue+                       Lentil.Parse.Issue Lentil.Parse.Syntaxes,+                       Lentil.Parse.Run   hs-source-dirs:      src   default-language:    Haskell2010 @@ -58,10 +59,12 @@                        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.Parse.Issue, Lentil.Parse.Syntaxes,+                       Lentil.Parse.Run,                        Lentil.ArgsSpec, Lentil.ExportSpec, Lentil.FileSpec,                        Lentil.PrintSpec, Lentil.Parse.SourceSpec,-                       Lentil.Parse.IssueSpec, Lentil.QuerySpec+                       Lentil.Parse.IssueSpec, Lentil.QuerySpec,+                       Lentil.TypeSpec, Lentil.Parse.RunSpec   type:                exitcode-stdio-1.0  source-repository head
src/Lentil/Args.hs view
@@ -63,7 +63,7 @@                     ( short 'f'      <>                       metavar "TYPE" <>                       value Pretty   <>-                      help "output format (pretty, tagpop, csv)" )+                      help "output format (pretty, tagpop, csv, comp)" )     where           parseFormat :: String -> ReadM Format           parseFormat s = let asl = map forTup (enumFrom minBound)
src/Lentil/Export.hs view
@@ -12,6 +12,7 @@ import Text.CSV import Lentil.Types +import qualified Data.List as L  --------------- -- FUNCTIONS --@@ -20,14 +21,28 @@ issues2CSV :: [Issue] -> String issues2CSV is = printCSV (firstLine : map i2r is)     where firstLine = ["Filepath", "Row", "Description", "Tags"]-          i2r i     = [iFile i, show (iRow i),+          i2r i     = [(prettyFP . iFile) i, show (iRow i),                        iDesc i, tags2String (iTags i)]  +-- compiler-like (gcc/ghc) warnings, parseable by emacs+issues2Compiler :: [Issue] -> String+issues2Compiler is = L.intercalate "\n" (map i2c is)+    where i2c i = (prettyFP . iFile) i ++ ":" ++ show (iRow i) ++ ":\n" +++                  "   " ++ iDesc i ++ " " +++                  tags2StringPretty (iTags i) ++ "\n"++ ----------------- -- ANCILLARIES -- ----------------- +-- tag1 tag2 etc tags2String :: [Tag] -> String tags2String ts = unwords (map tagString ts)++-- [tag1] [tag2] etc.+tags2StringPretty :: [Tag] -> String+tags2StringPretty ts = L.intercalate " " .+                       map ((openDel:) . (++[closeDel]) . tagString) $ ts 
src/Lentil/File.hs view
@@ -11,7 +11,7 @@ module Lentil.File where  import Lentil.Types-import Lentil.Parse.Issue+import Lentil.Parse.Run  import System.FilePath import System.FilePath.Find@@ -42,7 +42,9 @@ findClause :: [FilePath] -> [FilePath] -> FindClause Bool findClause i x = let ic     = mconcat $ map fp2fc i                      xc     = mconcat $ map fp2fc x-                 in fmap getAny ic &&? (not <$> fmap getAny xc)+                 in fileType ==? RegularFile &&?+                    fmap getAny ic           &&?+                    (not <$> fmap getAny xc)     where           fp2fc :: FilePath -> FindClause Any           fp2fc f = Any . L.isPrefixOf (combine "." f) <$> filePath
src/Lentil/Parse/Issue.hs view
@@ -10,32 +10,25 @@ 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 +import Prelude -- 7.8 hack --- 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] +-- 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 }+type ParIssue a = Parsec String () a  -- issue flagword data FlagWord = Todo | Fixme | Xxx deriving (Show, Enum, Eq)@@ -78,7 +71,6 @@ 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)@@ -114,8 +106,7 @@  -- optional ws + flagwords (case unsensitive) + optional ':' ++ optional1 ws incipit :: ParIssue FlagWord-incipit = char '\n' *> spaces >>-          fwpar               >>= \fw ->+incipit = fwpar               >>= \fw ->           optional (char ':') >>           many1 (char ' ')    >> -- fixme usa spaces non ' '           return fw@@ -126,9 +117,7 @@           fwpar = choice (map ciString flagWords) >>=                   return . s2fw --- TODO: allow tags in before description? [doc] [design] - ------------ -- ISSUES -- ------------@@ -140,7 +129,7 @@ -- another TODO). issue :: ParIssue Issue issue = (mkIssue <$> incipit-                 <*> fmap psPath getState+                 <*> fmap sourceName getPosition                  <*> (fmap sourceLine getPosition)                  <*> freeText                  <*> option [] (try tags))@@ -148,7 +137,6 @@     where           mkIssue fw fp ln ds tgs = Issue fp ln ds (addTag fw tgs) -          -- todo incipit should return FlagWord, not a string [refactor]           addTag Todo tgs = tgs           addTag fw   tgs = (Tag $ fw2s fw) : tgs @@ -159,50 +147,18 @@ freeText :: ParIssue Description freeText = fmap htmlify (manyTill anyChar end) <?> "free text"     where vp p      = try . parsecMap (const ()) $ p+          nlInc     = char '\n' *> spaces >> incipit           end       = lookAhead $ choice [vp (spaces *> tag),                                           vp blankline,-                                          vp incipit]--    -- todo rimuovi tutti gli eof, oramai non servono [refactor]+                                          vp nlInc,+                                          vp eof] -- fixme specifica meglio  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 = 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 
+ src/Lentil/Parse/Run.hs view
@@ -0,0 +1,77 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Lentil.Parse.Run+-- Copyright   :  © 2015 Francesco Ariis+-- License     :  GPLv3 (see the LICENSE file)+--+-- Parsing functions interface+-----------------------------------------------------------------------------+++module Lentil.Parse.Run where++import Lentil.Types+import Lentil.Parse.Issue+import Lentil.Parse.Source+import Lentil.Parse.Syntaxes++import Text.Parsec++import qualified System.IO as I+import qualified System.Directory as D++import Prelude -- 7.8 hack++++-- main function with wich to parse issues; errors on stderr+issueFinder :: [FilePath] -> IO [Issue]+issueFinder fps = fmap concat (mapM fileParser fps)++-- todo a function String -> [Issue] (w/o IO) [debug] [refactor]++-----------------+-- ANCILLARIES --+-----------------++-- errors to stderr+fileParser :: FilePath -> IO [Issue]+fileParser fp = D.doesFileExist fp >>= \fb ->++                -- file exists+                if fb == False+                  then perr (fp ++ " : no such file") >> return []+                  else++                -- parser exists+                case langParser fp of+                  Nothing -> return [] -- no error just empy list+                  Just p  -> readFile fp                >>=+                             runParIO p fp              >>=+                             parIssues fp . comms2Tuple+++-- generic parsing --++runParIO :: Parsec String () [a] -> FilePath -> String -> IO [a]+runParIO p fp t = case runParser p () fp t of+                    Left l  -> perr (fp ++ " : parse error " +++                                     show l) >> return []+                    Right r -> return r++-- output errors+perr :: String -> IO ()+perr cs  = I.hPutStrLn I.stderr cs+++-- issue parsing --++parIssues :: FilePath -> [Comment] -> IO [Issue]+parIssues fp cs = concat <$> mapM (parIssue fp) cs++parIssue :: FilePath -> Comment -> IO [Issue]+parIssue fp (r, t) = runParIO (setRow r >> issues) fp t++setRow :: Row -> ParIssue ()+setRow r = getPosition >>= setPosition . flip setSourceLine r+
src/Lentil/Parse/Source.hs view
@@ -10,21 +10,23 @@  module Lentil.Parse.Source where +import Lentil.Types+ 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 qualified Data.Char  as C+import qualified Data.Maybe as M+import qualified Data.List as L  import Control.Applicative hiding (many, (<|>)) +import Prelude -- 7.8 hack+ ----------- -- TYPES -- ----------- --- our parsers goal is simple: from a source file, only the comments and--- newlines+-- from source to comment strings type ParSource a = Parsec String () a  data ParSyntax = ParSyntax { psLineComms  :: [String],@@ -34,19 +36,43 @@                  deriving (Show)  ---------------- BLOCKS ---------------+-----------------+-- ANCILLARIES --+----------------- --- TODO: ++ "\n" is inefficient [lint] [refactor]-lineComment :: String -> ParSource String-lineComment s = string s *> fmap (++ "\n") (manyTill anyChar newline)+-- like many1 for manyTill+manyTill1 :: ParSource a -> ParSource b -> ParSource [a]+manyTill1 p ed = (:) <$> p <*> manyTill p ed+++--------------+-- COMMENTS --+--------------++data CommentString = SingleLine Row String  -- sans newline+                   | MultiLine Row String+                   deriving (Show, Eq)++-- parses stuff like "-- comment"+lineComment :: String -> ParSource CommentString+lineComment s = SingleLine                 <$>+                (string s *> getRow)       <*>+                (manyTill anyChar newline)                 <?> "line comment" -blockComment :: (String, String) -> ParSource String-blockComment (i, e) = string i *> manyTill anyChar (try $ string e)+blockComment :: (String, String) -> ParSource CommentString+blockComment (i, e) = MultiLine                         <$>+                      (string i *> getRow)              <*>+                      manyTill anyChar (try $ string e)                       <?> "block comment" +getRow :: ParSource Row+getRow = fmap sourceLine getPosition++------------------+-- OTHER BLOCKS --+------------------+ -- quoted strings, escaped by \, by mauke^ litString :: Char -> ParSource String litString ic = q *> many ((char '\\' *> anyChar) <|> noneOf no) <* q@@ -59,8 +85,6 @@ 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,@@ -70,109 +94,75 @@              <?> "program"     where           -- endp :: [ParSource String]-          endp  = lookAhead $ choice $ map try $ map string posts ++ [lchars]+          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)+          -- every "post" symbol (init comments, listring char)+          posts  = psLineComms ps              ++ -- line+                   map fst (psBlockComms ps)   ++ -- block+                   map (:[]) (psStringLits ps)    -- lit string            -- 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+sourcePart :: ParSyntax -> ParSource (Maybe CommentString)+sourcePart ps@(ParSyntax lc bc sl cl) = choice [plc, pbc, psl, pcl, ppr]+            <?> "source file part"+    where+          ct = choice . map try -sources :: ParSyntax -> ParSource String-sources ps =   fmap concat (many1 (source ps))-           <|> ("" <$ eof)+          plc = Just <$> (ct . map lineComment  $ lc)  -- line comlm+          pbc = Just <$> (ct . map blockComment $ bc)  -- block comm+          psl = Nothing <$ (choice . map try . map litString $ sl) -- str lit+          pcl = Nothing <$ (ct . map litChar $ cl)                 -- char lit+          ppr = Nothing <$ program ps                              -- program +-- ps: syntax to sever comment from the rest+source :: ParSyntax -> ParSource [CommentString]+source ps = fmap M.catMaybes (many1 (sourcePart ps)) <|> ([] <$ eof)+         <?> "source file" ------------------ SYNTAXES ----------------- --- TODO: add langparsers che sia estensibile e leggibile---       a compilazione [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+-------------+-- CONVERT --+------------- -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+type Comment = (Row, String)  ----------------- PARSERS ----------------+comms2Tuple :: [CommentString] -> [Comment]+comms2Tuple [] = []+comms2Tuple (c:cs) | isSl c    = let (a,z) = span isSl (c:cs)+                                 in groupLineComms a ++ comms2Tuple z+                   | otherwise = comment2Tuple c : comms2Tuple cs+    where+          isSl (SingleLine _ _) = True+          isSl _                = False +-- use it on Line comments **only**!+groupLineComms :: [CommentString] -> [Comment]+groupLineComms cs = let cs'         = map comment2Tuple cs+                        f (r, s) i  = (r, s, r-i) -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]+                        zipped      = zipWith f cs' (enumFrom 1)+                        grouped     = L.groupBy threeEq zipped +                    in map (flatComm . map back) grouped --- 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 ""+    where+          threeEq (_, _, x) (_, _, y) = x == y -          perr cs = I.hPutStrLn I.stderr cs+          back (a, b, _) = (a, b)+          flatComm cs'   = (fst . head $ cs',+                            unlines . map snd $ cs') -runParSource :: ParSource a -> FilePath -> String -> Either ParseError a-runParSource p fp cs = runParser p () fp cs+comment2Tuple :: CommentString -> Comment+comment2Tuple (SingleLine r t) = (r, t)+comment2Tuple (MultiLine  r t) = (r, t) 
+ src/Lentil/Parse/Syntaxes.hs view
@@ -0,0 +1,48 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Lentil.Parse.Syntaxes+-- Copyright   :  © 2015 Francesco Ariis+-- License     :  GPLv3 (see the LICENSE file)+--+-- Languages descriptors+-----------------------------------------------------------------------------+++module Lentil.Parse.Syntaxes where++import Lentil.Parse.Source+import Text.Parsec++import qualified System.FilePath as SF+++-- TODO: add langparsers che sia estensibile e leggibile+--       a compilazione [u:2] [feature:intermediate]++langParser :: String -> Maybe (ParSource [CommentString])+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++-- todo multiline signature? [lint]+-- todo tag at the beginning too? [design]+haskell, c, javascript, pascal, python, ruby, text :: ParSource [CommentString]+haskell    = source $ ParSyntax ["--"] [("{-", "-}")] ['"']  ['\'']+c          = source $ ParSyntax ["//"] [("/*", "*/")] ['"']  ['\'']+javascript = source $ ParSyntax ["//"] [("/*", "*/")] ['"', '\''] []+pascal     = source $ ParSyntax ["//"] [("{",  "}" ),+                                        ("(*", "*)")] ['\''] []+python     = source $ ParSyntax ["#"]  [("\"\"\"",+                                         "\"\"\"")]   ['"', '\''] []+ruby       = source $ ParSyntax ["#"] [("=begin",+                                         "=end")]      ['"', '\''] []+text       = (:[]) . MultiLine 1 <$> many anyChar+
src/Lentil/Print.hs view
@@ -40,9 +40,6 @@ -- PRETTY PRINT -- ------------------ --- TODO: colours are bad for unix philosophy, remove them? hlint---       manages to get a colorised output when ran alone but not when---       piped to less, how? [design] data TagCol = Red | Blue deriving (Eq) ppTag :: TagCol -> Tag -> Doc ppTag c t = col (char openDel) <> string (tagString t) <>@@ -63,7 +60,7 @@   ppFile :: Int -> [Issue] -> Doc-ppFile ind is = text (drop 2 $ iFile (head is)) PP.<$>+ppFile ind is = text (prettyFP $ iFile (head is)) PP.<$>                 vsep (map (ppIssue ind) is)  ppIssues :: Bool -> [Issue] -> String
src/Lentil/Types.hs view
@@ -10,7 +10,9 @@ module Lentil.Types where  +import qualified Data.List as L + data Issue = Issue { iFile   :: FilePath,                      iRow    :: Row,                      iDesc   :: Description,@@ -24,12 +26,18 @@ type Row = Int  -- output format-data Format = TagPop | Pretty | Csv+data Format = TagPop | Pretty | Csv | Comp             deriving (Show, Eq, Enum, Bounded)+    -- comp: as gcc/ghc would output warnings   -- tag delimiters openDel, closeDel :: Char openDel  = '[' closeDel = ']'++-- eliminate "./" at the beginning of fp if present+prettyFP :: String -> String+prettyFP fp | L.isPrefixOf "./" fp = drop 2 fp+            | otherwise            = fp 
src/Main.hs view
@@ -8,7 +8,8 @@ import Options.Applicative  import qualified Data.Maybe as M-import Data.Monoid+import Data.Monoid -- 7.8+import qualified System.IO as I   main :: IO ()@@ -27,26 +28,34 @@                                    short 'v'      <>                                    help "show version and copyright info" )     where-          versionCopy = "\nlentil - a frugal issue tracker, version 0.1.1.3\n\+          versionCopy = "lentil - a frugal issue tracker, version 0.1.2.0\n\                         \(C) 2015 Francesco Ariis - http://www.ariis.it\n\                         \released under the GNU General Public License 3\n" +-- todo add doc to use less -r when piping hIsTerminalDevice stdout [doc]  runLentil :: LOptions -> IO () runLentil lo = uncurry findIssues (loInExcl lo) >>= \is -> -- grab issues                let fil = filterAnd (loFilters lo) is       -- filtered-                   ord = chainSorts fil (loSort lo) in     -- ordered+                   ord = chainSorts fil (loSort lo)        -- ordered -               case loOutFile lo of+                   -- which function to use to output stuff (file/term)+                   outFunction :: String -> IO ()+                   outFunction = case loOutFile lo of+                                   Nothing -> putStrLn+                                   Just fp -> writeFile fp -                 -- std output-                 Nothing -> case loFormat lo of-                              Csv    -> putStrLn (issues2CSV is)-                              Pretty -> putStrLn (ppIssues True ord)-                              TagPop -> putStrLn (ppPopularity True is)+                   -- col or not?+                   bCol :: Bool+                   bCol = if loOutFile lo == Nothing+                            then True+                            else False -                 Just fp -> case loFormat lo of-                              Csv    -> writeFile fp (issues2CSV is)-                              Pretty -> writeFile fp (ppIssues False ord)-                              TagPop -> writeFile fp (ppPopularity False is)+               in++               case loFormat lo of+                 Pretty -> outFunction (ppIssues bCol ord)+                 TagPop -> outFunction (ppPopularity bCol fil)+                 Csv    -> outFunction (issues2CSV fil)+                 Comp   -> outFunction (issues2Compiler fil) 
test/Lentil/ExportSpec.hs view
@@ -8,7 +8,7 @@   is :: [Issue]-is = [Issue "file" 1 "da" [Tag "a", Tag "c"],+is = [Issue "./file" 1 "da" [Tag "a", Tag "c"],       Issue "file" 2 "db" [Tag "e:f"]]  csv :: String@@ -16,6 +16,13 @@       \\"file\",\"1\",\"da\",\"a c\"\n\       \\"file\",\"2\",\"db\",\"e:f\"" +cmp :: String+cmp = unlines $+        ["file:1:",+         "   da [a] [c]",+         "",+         "file:2:",+         "   db [e:f]"]  main :: IO () main = hspec spec@@ -23,7 +30,19 @@ spec :: Spec spec = do +  describe "tags2String" $ do+    it "converts tags to plain string, intercalated by ' '" $+      tags2String [Tag "a", Tag "b"] `shouldBe` "a b"++  describe "tags2StringPretty" $ do+    it "converts tags to string, with open/close delimiters" $+      tags2StringPretty [Tag "a", Tag "b"] `shouldBe` "[a] [b]"+   describe "issues2CSV" $ do     it "exports issues to CSV" $       issues2CSV is `shouldBe` csv++  describe "issues2Compiler" $ do+    it "exports issues to compiler-like output format" $+      issues2Compiler is `shouldBe` cmp 
test/Lentil/Parse/IssueSpec.hs view
@@ -3,18 +3,19 @@ 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 +import Prelude -- 7.8 hack+ -- 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)+                    (runParser p () fp cs)     where fp = "<f>"  @@ -56,23 +57,21 @@    describe "incipit" $ do     it "parses the initial section of an issue, returns nothing" $-       do sp incipit "\nTODO: "  `shouldBe` Just Todo-          sp incipit "\nFIXME: "  `shouldBe` Just Fixme-          sp incipit "\nXXX: "  `shouldBe` Just Xxx+       do sp incipit "TODO: "  `shouldBe` Just Todo+          sp incipit "FIXME: "  `shouldBe` Just Fixme+          sp incipit "XXX: "  `shouldBe` Just Xxx     it "doesn't work without previous newline" $-      sp incipit "\nTODO:some"  `shouldBe` Nothing+      sp incipit "TODO:some"  `shouldBe` Nothing     it "doesn't work if you don't add a space after :" $-      sp incipit "\nTODO:some"  `shouldBe` Nothing+      sp incipit "TODO:some"  `shouldBe` Nothing     it "does allow you to omit the :" $-      sp incipit "\nxxx some"  `shouldBe` Just Xxx+      sp incipit "xxx some"  `shouldBe` Just Xxx     it "is case unsensitive in flag-word" $-      sp incipit "\ntOdO some"  `shouldBe` Just Todo-    it "allows whitespace before the todo" $-      sp incipit "\n   fixme some"  `shouldBe` Just Fixme-    it "doesn't allow anything else before flag-words" $-      sp incipit "\nso fixme someday"  `shouldBe` Nothing+      sp incipit "tOdO some"  `shouldBe` Just Todo+    it "doesn't allow anything before flag-words" $+      sp incipit "so fixme someday"  `shouldBe` Nothing     it "fails if we are not at the beginning of line" $-      sp incipit "\nso fixme someday"  `shouldBe` Nothing+      sp incipit "so fixme someday"  `shouldBe` Nothing    describe "tags" $ do     it "parses a series of tags, sep by whitespace" $@@ -102,40 +101,39 @@    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 "fixme", 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+         `shouldBe` (Just $ Issue "<f>" 1 "this is it"+                                  [Tag "fixme", Tag "t", Tag "f:w"])     it "parses tagless/fieldless issues too" $-      sp issue "\nTODO: this is it\n"-         `shouldBe` (Just $ Issue "<f>" 2 "this is it" [])+      sp issue "TODO: this is it\n"+         `shouldBe` (Just $ Issue "<f>" 1 "this is it" [])+    it "parses an issue not ended by \\n" $+      sp issue "todo block1 "+         `shouldBe` (Just $ Issue "<f>" 1 "block1" [])     it "doesn't display the eventual \n at eof" $-      sp issue "\nTOdO: this is it\n"-         `shouldBe` (Just $ Issue "<f>" 2 "this is it" [])+      sp issue "TOdO: this is it\n"+         `shouldBe` (Just $ Issue "<f>" 1 "this is it" [])     it "doesn't accept a lone (naked) todo" $-      sp issue "\ntodo\n"+      sp issue "todo\n"          `shouldBe` Nothing     it "issues declared with fixme get a free [fixme] tag" $-      sp issue "\nfixme blah\n"-         `shouldBe` (Just $ Issue "<f>" 2 "blah" [Tag "fixme"])+      sp issue "fixme blah\n"+         `shouldBe` (Just $ Issue "<f>" 1 "blah" [Tag "fixme"])    describe "issues" $ do     it "parses multiple issues" $-      sp issues "\nTODO: this is it [f:w]\n TODO: hey [t]"+      sp issues "TODO: 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"  [Tag "fixme"],-                    Issue fp 4 "gamma" [Tag "xxx"],-                    Issue fp 6 "qqq"   [Tag "fixme", Tag "bug"]]+    it "parse multiple issues if on the same line" $+      sp issues "TODO: this is it [f:w] TODO: hey [t]"+         `shouldBe` Just [Issue "<f>" 1 "this is it" [Tag "f:w"],+                          Issue "<f>" 1 "hey" [Tag "t"]]+    it "don't parse multiple issues on the same line if not ended by tags" $+      sp issues "TODO: this is it todo hey [t]"+         `shouldBe` Just [Issue "<f>" 1 "this is it todo hey" [Tag "t"]]+    it "parses two issues, tagless, if the second starts on nl" $+      sp issues "TODO: this is it \n  todo hey [t]"+         `shouldBe` Just [Issue "<f>" 1 "this is it" [],+                          Issue "<f>" 2 "hey" [Tag "t"]] 
+ test/Lentil/Parse/RunSpec.hs view
@@ -0,0 +1,70 @@+module Lentil.Parse.RunSpec where++import Test.Hspec+import Text.Parsec ( runParser )++import Lentil.Types+import Lentil.Parse.Run+import Lentil.Parse.Source++import Prelude -- 7.8 hack++-- Parsing tests++-- simple parser (choosable if we are at begin of line or else)+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 "commentParser - specific" $ do+    it "doesn't parse contiguous line/block as a single issue" $+        fileParser "test/test-files/specific/contiguous.c"+            `shouldReturn` [Issue "test/test-files/specific/contiguous.c" 1+                                  "issue1" []]++  let spt fp = [Issue fp 1 "single comment" [],+                Issue fp 3 "single2" [],+                Issue fp 8 "block1"  [],+                Issue fp 9 "block2" [Tag "tog"]]+  describe "commentParser - languages" $ do+    it "parses a plain text source" $+        fileParser "test/test-files/lang-comm/text.txt"+            `shouldReturn` spt "test/test-files/lang-comm/text.txt"+    it "parses a haskell source" $+        fileParser "test/test-files/lang-comm/haskell.hs"+            `shouldReturn` spt "test/test-files/lang-comm/haskell.hs"+    it "parses a C source" $+        fileParser "test/test-files/lang-comm/clang.c"+            `shouldReturn` spt "test/test-files/lang-comm/clang.c"+    it "parses a Pascal source" $+        fileParser "test/test-files/lang-comm/pascal.pas"+            `shouldReturn` spt "test/test-files/lang-comm/pascal.pas"+    it "parses a javascript source" $+        fileParser "test/test-files/lang-comm/javascript.js"+            `shouldReturn` spt "test/test-files/lang-comm/javascript.js"+    it "parses a python source" $+        fileParser "test/test-files/lang-comm/python.py"+            `shouldReturn` spt "test/test-files/lang-comm/python.py"+    it "parses a ruby source" $+        fileParser "test/test-files/lang-comm/ruby.rb"+            `shouldReturn` spt "test/test-files/lang-comm/ruby.rb"++  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"  [Tag "fixme"],+                    Issue fp 4 "gamma" [Tag "xxx"],+                    Issue fp 6 "qqq"   [Tag "fixme", Tag "bug"]]+
test/Lentil/Parse/SourceSpec.hs view
@@ -4,10 +4,12 @@ import Text.Parsec (runParser) import Text.Parsec.Char import Text.Parsec.Combinator-import Control.Applicative +import Lentil.Types import Lentil.Parse.Source +import Prelude -- 7.8 hack+ -- Parsing tests  -- simple parser@@ -16,7 +18,12 @@                  (runParser p () fp cs)     where fp = "<f>" +-- short comment constructors+sl, ml :: Row -> String -> CommentString+sl r t = SingleLine r t+ml r t = MultiLine r t + main :: IO () main = hspec spec @@ -32,17 +39,20 @@       (sp (manyTill1 anyChar newline) "\n",        sp (manyTill  anyChar newline) "\n") `shouldBe` (Nothing,                                                         Just "")+    it "has the same behaviour as manyTill on 1 char w/o end char" $+      sp (manyTill1 anyChar newline) "f" `shouldBe`+      sp (manyTill anyChar newline) "f"    describe "lineComment" $ do     it "parses a line comment" $-      sp (lineComment "//") "// Test\n" `shouldBe` Just " Test\n"+      sp (lineComment "//") "// Test\n" `shouldBe` Just (sl 1 " Test")     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 "+         Just (ml 1 " Test\n2 ")    describe "litString" $ do     it "parses code string" $@@ -76,43 +86,33 @@   let rbs = ParSyntax ["#"] [] ['"', '\'']  []   describe "source" $ do     it "parses one piece of source (line-comment)" $-      sp (source hss) "-- hey\n my " `shouldBe`  Just " hey\n"+      sp (source hss) "-- hey\n my " `shouldBe` Just [sl 1 " hey"]     it "parses one piece of source (block-comment)" $-      sp (source hss) "{-hey-}\n my " `shouldBe`  Just "hey"+      sp (source hss) "{-hey-}\n my " `shouldBe` Just [ml 1 "hey"]     it "parses one piece of source (string-literal)" $-      sp (source hss) "\"hey\"" `shouldBe`  Just ""+      sp (source hss) "\"hey\"" `shouldBe` Just []     it "parses a string for language with ' and \" available" $-      sp (source rbs) "\"he#y\"" `shouldBe`  Just ""+      sp (source rbs) "\"he#y\"" `shouldBe` Just []     it "parses one piece of source (char-literal)" $-      sp (source hss) "\'h\'" `shouldBe`  Just ""+      sp (source hss) "\'h\'" `shouldBe` Just []     it "parses one piece of source (program instructions)" $-      sp (source hss) "prime'" `shouldBe`  Just ""--  describe "sources" $ do+      sp (source hss) "prime'" `shouldBe` Just []     it "should not choke on en empty file" $-      sp (sources hss) "" `shouldBe`  Just ""+      sp (source hss) "" `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)+  describe "groupLineComms" $ do+    it "groups line comments" $+      groupLineComms [SingleLine 1 "a",+                      SingleLine 2 "b",+                      SingleLine 4 "c"] `shouldBe` [(1, "a\nb\n"),+                                                    (4, "c\n"   )]++  describe "comms2Tuple" $ do+    it "groups comments" $+      comms2Tuple [SingleLine 1 "a",+                   SingleLine 2 "b",+                   MultiLine  5 "c",+                   SingleLine 6 "d"] `shouldBe` [(1, "a\nb\n"),+                                                 (5, "c"     ),+                                                 (6, "d\n"   )] 
+ test/Lentil/TypeSpec.hs view
@@ -0,0 +1,20 @@+module Lentil.TypeSpec where++import Test.Hspec++import Lentil.Types++-- File tests++main :: IO ()+main = hspec spec++spec :: Spec+spec = do++  describe "prettyFP" $ do+    it "eliminates ./ from beginning of fp, if present" $+      prettyFP "./file" `shouldBe` "file"+    it "doesn't change fp is ./ is not there at begin-of-fp" $+      prettyFP "./file" `shouldBe` "file"+
test/test-files/lang-comm/clang.c view
@@ -1,11 +1,12 @@-// single+// todo single // comment+// Todo: single2  var c = "st/*ring"; // string+char letter = 'c' // char -/* block-   comment */+/* TODO: block1 */+/* TODO: block2 [tog] */ -char letter = 'c' // char  //  end
test/test-files/lang-comm/haskell.hs view
@@ -1,11 +1,9 @@--- single+-- TODO: single -- comment+-- todo single2  someOther = "  \"  " -- string--{- block-   comment -}- char' = '\n' -- char ---  end+{- todo block1 -}+{- todo block2 [tog] -}
test/test-files/lang-comm/javascript.js view
@@ -1,11 +1,12 @@-// single+// todo single // comment+// Todo: single2  var c = "st/*ring"; // string--/* block-   comment */- var letter = 'c' // char++/* todo block1 */+          /* todo block2 [tog]+          */  //  end
test/test-files/lang-comm/pascal.pas view
@@ -1,11 +1,11 @@-// single+// todo single // comment+// Todo: single2  qqq := 'Afla' // string--{ block}-(*   comment *)- c := '"' // char++{ TODO: block1 }+(*   todo block2 [tog] *)  //  end
test/test-files/lang-comm/python.py view
@@ -1,11 +1,11 @@-# single+# TODO: single # comment+# todo single2  c = "st/*ring" # string--""" block-   comment """- letter = 'c' # char++""" todo block1+   TODO: block2 [tog] """  #  end
test/test-files/lang-comm/ruby.rb view
@@ -1,11 +1,11 @@-# single+# TODO: single # comment+# todo single2  puts "str#ing" # string--=begin block-   comment =end- a = [1, 'hi', 3.14, 1, 2, [4, 5]] # char++=begin todo block1+   todo block2 [tog] =end  #  end
test/test-files/lang-comm/text.txt view
@@ -1,11 +1,9 @@- single- comment+todo single+comment+TODO single2   string - block-   comment  - char--  end+   todo block1+       todo  block2 [tog]