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 +11/−1
- doc/usr/page.rst +28/−9
- issues.txt +45/−35
- lentil.cabal +7/−4
- src/Lentil/Args.hs +1/−1
- src/Lentil/Export.hs +16/−1
- src/Lentil/File.hs +4/−2
- src/Lentil/Parse/Issue.hs +11/−55
- src/Lentil/Parse/Run.hs +77/−0
- src/Lentil/Parse/Source.hs +86/−96
- src/Lentil/Parse/Syntaxes.hs +48/−0
- src/Lentil/Print.hs +1/−4
- src/Lentil/Types.hs +9/−1
- src/Main.hs +22/−13
- test/Lentil/ExportSpec.hs +20/−1
- test/Lentil/Parse/IssueSpec.hs +37/−39
- test/Lentil/Parse/RunSpec.hs +70/−0
- test/Lentil/Parse/SourceSpec.hs +35/−35
- test/Lentil/TypeSpec.hs +20/−0
- test/test-files/lang-comm/clang.c +5/−4
- test/test-files/lang-comm/haskell.hs +4/−6
- test/test-files/lang-comm/javascript.js +6/−5
- test/test-files/lang-comm/pascal.pas +5/−5
- test/test-files/lang-comm/python.py +5/−5
- test/test-files/lang-comm/ruby.rb +5/−5
- test/test-files/lang-comm/text.txt +5/−7
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]