8 -- search for definitions of things
9 -- we do this by looking for the following patterns:
10 -- data XXX = ... giving a datatype location
11 -- newtype XXX = ... giving a newtype location
12 -- bla :: ... giving a function location
14 -- by doing it this way, we avoid picking up local definitions
15 -- (whether this is good or not is a matter for debate)
18 -- We generate both CTAGS and ETAGS format tags files
19 -- The former is for use in most sensible editors, while EMACS uses ETAGS
25 filedata <- mapM findthings filenames
26 ctagsfile <- openFile "tags" WriteMode
27 etagsfile <- openFile "TAGS" WriteMode
28 writectagsfile ctagsfile filedata
29 writeetagsfile etagsfile filedata
33 type FileName = String
35 type ThingName = String
37 -- The position of a token or definition
42 String -- string that makes up that line
45 -- A definition we have found
46 data FoundThing = FoundThing ThingName Pos
49 -- Data we have obtained from a file
50 data FileData = FileData FileName [FoundThing]
52 data Token = Token String Pos
56 -- stuff for dealing with ctags output format
58 writectagsfile :: Handle -> [FileData] -> IO ()
59 writectagsfile ctagsfile filedata = do
60 let things = concat $ map getfoundthings filedata
61 mapM_ (\x -> hPutStrLn ctagsfile $ dumpthing x) things
63 getfoundthings :: FileData -> [FoundThing]
64 getfoundthings (FileData filename things) = things
66 dumpthing :: FoundThing -> String
67 dumpthing (FoundThing name (Pos filename line _ _)) =
68 name ++ "\t" ++ filename ++ "\t" ++ (show $ line + 1)
71 -- stuff for dealing with etags output format
73 writeetagsfile :: Handle -> [FileData] -> IO ()
74 writeetagsfile etagsfile filedata = do
75 mapM_ (\x -> hPutStr etagsfile $ e_dumpfiledata x) filedata
77 e_dumpfiledata :: FileData -> String
78 e_dumpfiledata (FileData filename things) =
79 "\x0c\n" ++ filename ++ "," ++ (show thingslength) ++ "\n" ++ thingsdump
81 thingsdump = concat $ map e_dumpthing things
82 thingslength = length thingsdump
84 e_dumpthing :: FoundThing -> String
85 e_dumpthing (FoundThing name (Pos filename line token fullline)) =
86 (concat $ take (token + 1) $ spacedwords fullline)
87 ++ "\x7f" ++ (show line) ++ "," ++ (show $ line+1) ++ "\n"
90 -- like "words", but keeping the whitespace, and so letting us build
93 spacedwords :: String -> [String]
95 spacedwords xs = (blanks ++ wordchars):(spacedwords rest2)
97 (blanks,rest) = span Char.isSpace xs
98 (wordchars,rest2) = span (\x -> not $ Char.isSpace x) rest
101 -- Find the definitions in a file
103 findthings :: FileName -> IO FileData
104 findthings filename = do
105 text <- readFile filename
106 let aslines = lines text
107 let wordlines = map words aslines
108 let noslcoms = map stripslcomments wordlines
109 let tokens = concat $ zipWith3 (withline filename) noslcoms
111 let nocoms = stripblockcomments tokens
112 return $ FileData filename $ findstuff nocoms
114 -- Create tokens from words, by recording their line number
115 -- and which token they are through that line
117 withline :: FileName -> [String] -> String -> Int -> [Token]
118 withline filename words fullline i =
119 zipWith (\w t -> Token w (Pos filename i t fullline)) words $ [0 ..]
121 -- comments stripping
123 stripslcomments :: [String] -> [String]
124 stripslcomments ("--":xs) = []
125 stripslcomments (x:xs) = x : stripslcomments xs
126 stripslcomments [] = []
128 stripblockcomments :: [Token] -> [Token]
129 stripblockcomments ((Token "\\end{code}" _):xs) = afterlitend xs
130 stripblockcomments ((Token "{-" _):xs) = afterblockcomend xs
131 stripblockcomments (x:xs) = x:stripblockcomments xs
132 stripblockcomments [] = []
134 afterlitend2 :: [Token] -> [Token]
135 afterlitend2 (x:xs) = afterlitend xs
138 afterlitend :: [Token] -> [Token]
139 afterlitend ((Token "\\begin{code}" _):xs) = xs
140 afterlitend (x:xs) = afterlitend xs
143 afterblockcomend :: [Token] -> [Token]
144 afterblockcomend ((Token token _):xs) | contains "-}" token = xs
145 | otherwise = afterblockcomend xs
146 afterblockcomend [] = []
149 -- does one string contain another string
151 contains :: Eq a => [a] -> [a] -> Bool
152 contains sub full = any (isPrefixOf sub) $ tails full
155 ints i = i:(ints $ i+1)
158 -- actually pick up definitions
160 findstuff :: [Token] -> [FoundThing]
161 findstuff ((Token "data" _):(Token name pos):xs) =
162 FoundThing name pos : (getcons xs) ++ (findstuff xs)
163 findstuff ((Token "newtype" _):(Token name pos):xs) =
164 FoundThing name pos : findstuff xs
165 findstuff ((Token "type" _):(Token name pos):xs) =
166 FoundThing name pos : findstuff xs
167 findstuff ((Token name pos):(Token "::" _):xs) =
168 FoundThing name pos : findstuff xs
169 findstuff (x:xs) = findstuff xs
173 -- get the constructor definitions, knowing that a datatype has just started
175 getcons :: [Token] -> [FoundThing]
176 getcons ((Token "=" _):(Token name pos):xs) =
177 FoundThing name pos : getcons2 xs
178 getcons (x:xs) = getcons xs
182 getcons2 ((Token "=" _):xs) = []
183 getcons2 ((Token "|" _):(Token name pos):xs) =
184 FoundThing name pos : getcons2 xs
185 getcons2 (x:xs) = getcons2 xs