packages feed

hasktags (empty) → 0.67

raw patch · 5 files changed

+368/−0 lines, 5 filesdep +basedep +haskell98setup-changed

Dependencies added: base, haskell98

Files

+ HaskTags.hs view
@@ -0,0 +1,279 @@++module Main (main) where++import Char+import List+import IO+import System.Environment+import System.Console.GetOpt+import System.Exit+++-- search for definitions of things+-- we do this by looking for the following patterns:+-- data XXX = ...      giving a datatype location+-- newtype XXX = ...   giving a newtype location+-- bla :: ...          giving a function location+--+-- by doing it this way, we avoid picking up local definitions+--              (whether this is good or not is a matter for debate)+--++-- We generate both CTAGS and ETAGS format tags files+-- The former is for use in most sensible editors, while EMACS uses ETAGS++--+-- TODO add tag categories+-- alternatives: http://haskell.org/haskellwiki/Tags++main :: IO ()+main = do+        progName <- getProgName+        args <- getArgs+        let usageString = "Usage: " ++ progName ++ " [OPTION...] [files...]"+        let (modes, filenames, errs) = getOpt Permute options args+        if errs /= [] || elem Help modes || filenames == []+         then do+           putStr $ unlines errs+           putStr $ usageInfo usageString options+           exitWith (ExitFailure 1)+         else return ()+        let mode = getMode (Append `delete` modes)+        let openFileMode = if elem Append modes+                           then AppendMode+                           else WriteMode+        filedata <- mapM findthings filenames+        if mode == BothTags || mode == CTags+         then do+           ctagsfile <- openFile "tags" openFileMode+           writectagsfile ctagsfile filedata+           hClose ctagsfile+         else return ()+        if mode == BothTags || mode == ETags+         then do+           etagsfile <- openFile "TAGS" openFileMode+           writeetagsfile etagsfile filedata+           hClose etagsfile+         else return ()++-- | getMode takes a list of modes and extract the mode with the+--   highest precedence.  These are as follows: Both, CTags, ETags+--   The default case is Both.+getMode :: [Mode] -> Mode+getMode [] = BothTags+getMode [x] = x+getMode (x:xs) = max x (getMode xs)+++data Mode = ETags | CTags | BothTags | Append | Help deriving (Ord, Eq, Show)++options :: [OptDescr Mode]+options = [ Option "c" ["ctags"]+            (NoArg CTags) "generate CTAGS file (ctags)"+          , Option "e" ["etags"]+            (NoArg ETags) "generate ETAGS file (etags)"+          , Option "b" ["both"]+            (NoArg BothTags) ("generate both CTAGS and ETAGS")+          , Option "a" ["append"]+            (NoArg Append) ("append to existing CTAGS and/or ETAGS file(s)")+          , Option "h" ["help"] (NoArg Help) "This help"+          ]++type FileName = String++type ThingName = String++-- The position of a token or definition+data Pos = Pos+                FileName -- file name+                Int      -- line number+                Int      -- token number+                String   -- string that makes up that line+    deriving (Show, Eq)++-- A definition we have found+data FoundThing = FoundThing ThingName Pos+    deriving (Show, Eq)++-- Data we have obtained from a file+data FileData = FileData FileName [FoundThing]++data Token = Token String Pos+    deriving Show+++-- stuff for dealing with ctags output format++writectagsfile :: Handle -> [FileData] -> IO ()+writectagsfile ctagsfile filedata = do+    let things = concat $ map getfoundthings filedata+    mapM_ (\x -> hPutStrLn ctagsfile $ dumpthing x) (sortThings things)++sortThings :: [FoundThing] -> [FoundThing]+sortThings = sortBy (\(FoundThing a _) (FoundThing b _) -> compare a b)++getfoundthings :: FileData -> [FoundThing]+getfoundthings (FileData _ things) = things++dumpthing :: FoundThing -> String+dumpthing (FoundThing name (Pos filename line _ _)) =+    name ++ "\t" ++ filename ++ "\t" ++ (show $ line + 1)+++-- stuff for dealing with etags output format++writeetagsfile :: Handle -> [FileData] -> IO ()+writeetagsfile etagsfile filedata = do+    mapM_ (\x -> hPutStr etagsfile $ e_dumpfiledata x) filedata++e_dumpfiledata :: FileData -> String+e_dumpfiledata (FileData filename things) =+    "\x0c\n" ++ filename ++ "," ++ (show thingslength) ++ "\n" ++ thingsdump+    where thingsdump = concat $ map e_dumpthing things+          thingslength = length thingsdump++e_dumpthing :: FoundThing -> String+e_dumpthing (FoundThing _ (Pos _ line token fullline)) =+    (concat $ take (token + 1) $ spacedwords fullline)+ ++ "\x7f" ++ (show line) ++ "," ++ (show $ line+1) ++ "\n"+++-- like "words", but keeping the whitespace, and so letting us build+-- accurate prefixes++spacedwords :: String -> [String]+spacedwords [] = []+spacedwords xs = (blanks ++ wordchars):(spacedwords rest2)+    where (blanks,rest) = span Char.isSpace xs+          (wordchars,rest2) = span (\x -> not $ Char.isSpace x) rest+++-- Find the definitions in a file++findthings :: FileName -> IO FileData+findthings filename = do+    text <- readFile filename+    evaluate text -- forces evaluation of text+                  -- too many files were being opened otherwise since+                  -- readFile is lazy+    let aslines = lines text+    let wordlines = map mywords aslines+    let noslcoms = map stripslcomments wordlines+    let tokens = concat $ zipWith3 (withline filename) noslcoms aslines [0 ..]+    -- there are some tokens with "" (don't know why yet) this filter fixes it+    let tokens' = filter (\(Token s _ ) -> (not .  null) s ) tokens+    let nocoms = stripblockcomments tokens'+    -- using nub because getcons and findstuff are parsing parts of the file twice+    return $ FileData filename $ nub $ findstuff nocoms+  where evaluate [] = return ()+        evaluate (c:cs) = c `seq` evaluate cs+        -- my words is mainly copied from Data.List.+        -- difference abc::def is split into three words instead of one.+        -- We should really be lexing Haskell properly here rather+        -- than using hacks like this. In the future we expect hasktags+        -- to be replaced by something using the GHC API.+        mywords :: String -> [String]+        mywords (':':':':xs) = "::" : mywords xs+        mywords s =  case dropWhile isSpace s of+                         "" -> []+                         s' -> w : mywords s''+                             where (w, s'') = myBreak s'+                                   myBreak [] = ([],[])+                                   myBreak (':':':':xs) = ([], "::"++xs)+                                   myBreak (' ':xs) = ([],xs);+                                   myBreak (x:xs) = let (a,b) = myBreak xs+                                                    in  (x:a,b)++-- Create tokens from words, by recording their line number+-- and which token they are through that line++withline :: FileName -> [String] -> String -> Int -> [Token]+withline filename theWords fullline i =+    zipWith (\w t -> Token w (Pos filename i t fullline)) theWords $ [0 ..]++-- comments stripping++stripslcomments :: [String] -> [String]+stripslcomments ("--" : _) = []+stripslcomments (x : xs) = x : stripslcomments xs+stripslcomments [] = []++stripblockcomments :: [Token] -> [Token]+stripblockcomments ((Token "\\end{code}" _):xs) = afterlitend xs+stripblockcomments ((Token "{-" _):xs) = afterblockcomend xs+stripblockcomments (x:xs) = x:stripblockcomments xs+stripblockcomments [] = []++afterlitend :: [Token] -> [Token]+afterlitend (Token "\\begin{code}" _ : xs) = xs+afterlitend (_ : xs) = afterlitend xs+afterlitend [] = []++afterblockcomend :: [Token] -> [Token]+afterblockcomend ((Token token _):xs)+ | contains "-}" token = xs+ | otherwise           = afterblockcomend xs+afterblockcomend [] = []+++-- does one string contain another string++contains :: Eq a => [a] -> [a] -> Bool+contains sub full = any (isPrefixOf sub) $ tails full++-- actually pick up definitions++findstuff :: [Token] -> [FoundThing]+findstuff ((Token "module" _):(Token name pos):xs) =+    FoundThing name pos : (getcons xs) ++ (findstuff xs)+findstuff ((Token "data" _):(Token name pos):xs) =+    FoundThing name pos : (getcons xs) ++ (findstuff xs)+findstuff ((Token "newtype" _):(Token name pos):xs) =+    FoundThing name pos : findstuff xs+findstuff ((Token "type" _):(Token name pos):xs) =+    FoundThing name pos : findstuff xs+findstuff ((Token "class" _):xs) = findClassName xs+findstuff ((Token name pos):(Token "::" _):xs) =+    FoundThing name pos : findstuff xs+findstuff (_ : xs) = findstuff xs+findstuff [] = []++findClassName :: [Token] -> [FoundThing]+findClassName []  = []+findClassName [Token n p]  = [FoundThing n p]+findClassName xs = (\(Token n pos : xs') -> FoundThing n pos : findstuff xs') . drop2 . dropParens 0 $ xs++dropParens :: Integer -> [Token] -> [Token]+dropParens n (Token "(" _ : xs) = dropParens (n + 1) xs+dropParens 0 (x           : xs) = x : xs+dropParens 1 (Token ")" _ : xs) = xs+dropParens n (Token ")" _ : xs) = dropParens (n - 1) xs+dropParens n (_           : xs) = dropParens n xs+dropParens _ []                 = [] -- Shouldn't happen on correct source++-- dropsEverything till token "=>" (if it is on the same line as the+-- first token. if not return tokens)+drop2 :: [Token] -> [Token]+drop2 tokens@(Token _ (Pos _ line_nr _ _ ) : _) =+  let (line, following) = span (\(Token _ (Pos _ l _ _)) -> l == line_nr) tokens+      (_, following_in_line) = span (\(Token n _) -> n /= "=>") line+  in case following_in_line of+          (Token "=>" _ : xs) -> xs ++ following+          _ -> tokens+drop2 xs = xs++-- get the constructor definitions, knowing that a datatype has just started++getcons :: [Token] -> [FoundThing]+getcons (Token "=" _ : Token name pos : xs) =+    FoundThing name pos : getcons2 xs+getcons (_ : xs) = getcons xs+getcons [] = []++getcons2 :: [Token] -> [FoundThing]+getcons2 (Token "=" _ : _) = []+getcons2 (Token "|" _ : Token name pos : xs) =+    FoundThing name pos : getcons2 xs+getcons2 (_:xs) = getcons2 xs+getcons2 [] = []+
+ LICENSE view
@@ -0,0 +1,31 @@+The Glasgow Haskell Compiler License++Copyright 2002, The University Court of the University of Glasgow.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+this list of conditions and the following disclaimer.++- Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.++- Neither name of the University nor the names of its contributors may be+used to endorse or promote products derived from this software without+specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF+GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+DAMAGE.
+ README view
@@ -0,0 +1,33 @@++"hasktags" is a very simple Haskell program that produces ctags "tags" and etags "TAGS" files for Haskell programs.++As such, it does essentially the same job that hstags and fptags used to do, but, both of those seem to no longer be maintained, and it seemed to be easier to write my own version rather than to get one of them to work.++Example usage:++find -name \*.\*hs | xargs hasktags+++This will create "tags" and "TAGS" files in the current directory describing all Haskell files in the current directory or below.++++Features+	* Includes top level functions, provided a type signature is given+	* Includes data declarations, and constructors+	* Includes newtypes+	+	- But sometimes gets things wrong or misses things out+		It's only a simple program+		+		+Using with your editor:++With NEdit+	Load the "tags" file using File/Load Tags File.+	Use "Ctrl-D" to search for a tag.++With XEmacs/Emacs+	Load the "TAGS" file using "visit-tags-table"+	Use "M-." to search for a tag.+	
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Distribution.Simple++main :: IO ()+main = defaultMain
+ hasktags.cabal view
@@ -0,0 +1,19 @@+Name: hasktags+Version: 0.67+Copyright: The University Court of the University of Glasgow+License: BSD3+License-File: LICENSE+Author: The GHC Team+Maintainer: libraries@haskell.org+Synopsis: Produces ctags "tags" and etags "TAGS" files for Haskell programs+Description:+	Produces ctags "tags" and etags "TAGS" files for Haskell programs.+Category: Development+build-type: Simple+cabal-version: >=1.2+extra-source-files: README++Executable hasktags+    Main-Is: HaskTags.hs+    Build-Depends: haskell98, base+