packages feed

lushtags (empty) → 0.0.1

raw patch · 9 files changed

+480/−0 lines, 9 filesdep +basedep +haskell-src-extsdep +textsetup-changedbinary-added

Dependencies added: base, haskell-src-exts, text, vector

Files

+ AUTHORS view
@@ -0,0 +1,1 @@+Bit Connor <bit@mutantlemon.com>
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (C) 2011 The lushtags Authors (see AUTHORS file)++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ README.md view
@@ -0,0 +1,60 @@+lushtags+========++Create ctags compatible tags files for Haskell programs++Similar programs are [hasktags][1], [gasbag][2], [hothasktags][3], and GHC's+builtin ctags generation.++lushtags differs from these by being designed to have smooth integration with+the [Vim Tagbar][4] plugin.++Tagbar is nice because it deals with creating tags automatically. There is no+need to manually run commands or keep track of tag files. Just open any Haskell+file in Vim and the Tagbar window will instantly show an interactive browsable+list of all the functions and declarations in the file. This window also+updates automatically as you edit the file.++The tags created by lushtags are marked with several extensions, so that when+used with Tagbar you get these features:++- Type signatures are displayed for functions.+- Tags are properly scoped so that, for example, data declarations appear as a+  tree with their constructors scoped as children.+- Definitions that are exported from the module are marked as "public" and+  appear emphasized in Tagbar.+- Tag locations are internally stored as patterns, not line numbers, so that+  you can correctly jump to tags even if they have moved in the source code+  during editing.++![screenshot](https://github.com/bitc/lushtags/raw/master/doc/screenshot-tagbar-2011-09-19.png)++[1]: http://hackage.haskell.org/package/hasktags+[2]: http://kingfisher.nfshost.com/sw/gasbag/+[3]: http://hackage.haskell.org/package/hothasktags+[4]: http://majutsushi.github.com/tagbar/++Using lushtags with Vim and the Tagbar plugin+---------------------------------------------++1. Build and install the lushtags executable++    $ cabal configure+    $ cabal build+    $ cabal install++2. Install the Tagbar plugin. Tagbar can be found at++    <http://www.vim.org/scripts/script.php?script_id=3465>  +    <http://majutsushi.github.com/tagbar/>++3. Install the included Haskell Tagbar configuration:++    $ cp util/tagbar-haskell.vim ~/.vim/plugin/++4. Try it out:++    $ vim Hello.hs++Now open the Tagbar with the command `:TagbarOpen`. An interactive sidebar will+appear with all of the tags in your Haskell source file.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ doc/screenshot-tagbar-2011-09-19.png view

binary file changed (absent → 87849 bytes)

+ lushtags.cabal view
@@ -0,0 +1,59 @@+name:                lushtags+version:             0.0.1+cabal-version:       >=1.10+build-type:          Simple+license:             MIT+license-file:        LICENSE+author:              Bit Connor+homepage:            https://github.com/bitc/lushtags+maintainer:          bit@mutantlemon.com+category:            Development+synopsis:            Create ctags compatible tags files for Haskell programs+description:+    > Create ctags compatible tags files for Haskell programs+    >+    > Similar programs are [hasktags][1], [gasbag][2], [hothasktags][3], and GHC's+    > builtin ctags generation.+    >+    > lushtags differs from these by being designed to have smooth integration with+    > the [Vim Tagbar][4] plugin.+    >+    > Tagbar is nice because it deals with creating tags automatically. There is no+    > need to manually run commands or keep track of tag files. Just open any Haskell+    > file in Vim and the Tagbar window will instantly show an interactive browsable+    > list of all the functions and declarations in the file. This window also+    > updates automatically as you edit the file.+    >+    > The tags created by lushtags are marked with several extensions, so that when+    > used with Tagbar you get these features:+    >+    > - Type signatures are displayed for functions.+    > - Tags are properly scoped so that, for example, data declarations appear as a+    >   tree with their constructors scoped as children.+    > - Definitions that are exported from the module are marked as "public" and+    >   appear emphasized in Tagbar.+    > - Tag locations are internally stored as patterns, not line numbers, so that+    >   you can correctly jump to tags even if they have moved in the source code+    >   during editing.+    >+    > ![screenshot](https://github.com/bitc/lushtags/raw/master/doc/screenshot-tagbar-2011-09-19.png)+    >+    > [1]: http://hackage.haskell.org/package/hasktags+    > [2]: http://kingfisher.nfshost.com/sw/gasbag/+    > [3]: http://hackage.haskell.org/package/hothasktags+    > [4]: http://majutsushi.github.com/tagbar/++source-repository head+  type:              git+  location:          git://github.com/bitc/lushtags.git++executable lushtags+  hs-source-dirs:    src+  main-is:           Main.hs+  other-modules:     Tags+  default-language:  Haskell98+  ghc-options:       -Wall+  build-depends:     base >= 4 && < 5,+                     vector == 0.9.*,+                     text == 0.11.*,+                     haskell-src-exts == 1.11.*
+ src/Main.hs view
@@ -0,0 +1,78 @@+----------------------------------------------------------------------------+-- |+-- Module      :  Main+-- License     :  MIT (see LICENSE)+-- Authors     :  Bit Connor <bit@mutantlemon.com>+--+-- Maintainer  :  Bit Connor <bit@mutantlemon.com>+-- Stability   :  unstable+-- Portability :  portable+--+-- lushtags, Create ctags compatible tags files for Haskell programs+--+-----------------------------------------------------------------------------++module Main (main) where++import Data.List (isPrefixOf, partition)+import Data.Vector(Vector, fromList)+import Language.Haskell.Exts.Annotated (parseFileContentsWithMode, ParseMode(..), knownExtensions, ParseResult(ParseOk, ParseFailed))+import System.Environment (getArgs, getProgName)+import System.IO (hPutStrLn, stderr)+import qualified Data.Text as T (unpack, lines, pack, unlines)+import qualified Data.Text.IO as T (readFile, putStr)++import Tags (Tag, createTags, tagToString)++main :: IO ()+main = do+    rawArgs <- getArgs+    let (options, files) = getOptions rawArgs+        ignore_parse_error = "--ignore-parse-error" `elem` options+    case files of+        [] -> do+            progName <- getProgName+            hPutStrLn stderr $ "Usage: " ++ progName ++ " [options] [--] <file>"+        filename:_ -> processFile filename ignore_parse_error >>= printTags++printTags :: [Tag] -> IO ()+printTags tags =+    T.putStr $ T.unlines $ map (T.pack . tagToString) tags++processFile :: FilePath -> Bool -> IO [Tag]+processFile file ignore_parse_error = do+    (fileContents, fileLines) <- loadFile file+    case parseFileContentsWithMode parseMode fileContents of+        ParseFailed loc message ->+            if ignore_parse_error then+                return []+            else+                -- TODO Better error reporting+                fail $ "Parse error: " ++ show loc ++ ": " ++ message+        ParseOk parsedModule -> return $ createTags (parsedModule, fileLines)+    where+        parseMode = ParseMode+            { parseFilename = file+            , extensions = knownExtensions+            , ignoreLanguagePragmas = False+            , ignoreLinePragmas = True+            , fixities = Nothing+            }++loadFile :: FilePath -> IO (String, Vector String)+loadFile file = do+    text <- T.readFile file+    let fullContents = T.unpack text+    let textLines = T.lines text+    let stringLines = map T.unpack textLines+    let fileLines = fromList stringLines+    return (fullContents, fileLines)++getOptions :: [String] -> ([String], [String])+getOptions args =+    let (start, end) = span (/= "--") args+        (options, nonOptions) = partition (isPrefixOf "-") start+    in (options, nonOptions ++ dropSep end)+    where+        dropSep ("--":xs) = xs+        dropSep xs        = xs
+ src/Tags.hs view
@@ -0,0 +1,218 @@+----------------------------------------------------------------------------+-- |+-- Module      :  Tags+-- License     :  MIT (see LICENSE)+-- Authors     :  Bit Connor <bit@mutantlemon.com>+--+-- Maintainer  :  Bit Connor <bit@mutantlemon.com>+-- Stability   :  unstable+-- Portability :  portable+--+-- Extract Tags from a Haskell Module+--+-----------------------------------------------------------------------------++module Tags+    (+      Tag(..)+    , createTags+    , tagToString+    ) where++import Data.Vector(Vector, (!))+import Language.Haskell.Exts.Annotated (SrcSpan(..), SrcSpanInfo(..))+import Language.Haskell.Exts.Annotated.Syntax+import Language.Haskell.Exts.Pretty (prettyPrintStyleMode, Style(..), Mode(OneLineMode), defaultMode)++data Tag = Tag+    { tagName :: String+    , tagFile :: String+    , tagPattern :: String+    , tagKind :: TagKind+    , tagLine :: Int+    , tagParent :: Maybe (TagKind, String)+    , tagSignature :: Maybe String+    , tagAccess :: Maybe TagAccess+    }+    deriving (Eq, Ord, Show)++data TagKind+    = TModule+    | TExport+    | TImport+    | TType+    | TData+    | TNewType+    | TConstructor+    | TFunction+    deriving (Eq, Ord, Show)++-- Access modifiers comes from the C++ world. Haskell doesn't have them, but+-- they are useful for marking our tags with similar meanings. For example,+-- marking all exported functions as public, or marking qualified imports+-- differently from unqualified ones.+data TagAccess+    = AccessPublic+    | AccessPrivate+    | AccessProtected+    deriving (Eq, Ord, Show)++-- First letter of each kind name must be unique!+tagKindName :: TagKind -> String+tagKindName TModule = "module"+tagKindName TExport = "export"+tagKindName TImport = "import"+tagKindName TType = "type"+tagKindName TData = "data"+tagKindName TNewType = "newtype"+tagKindName TConstructor = "constructor"+tagKindName TFunction = "function"++tagKindLetter :: TagKind -> Char+tagKindLetter = head . tagKindName++tagToString :: Tag -> String+tagToString tag =+    let parentStr = case tagParent tag of+            Nothing -> ""+            Just (kind, name) -> "\t" ++ tagKindName kind ++ ":" ++ name+        signatureStr = case tagSignature tag of+            Nothing -> ""+            Just sig -> "\tsignature:("++ sig ++ ")"+        accessStr = case tagAccess tag of+            Nothing -> ""+            Just AccessPublic -> "\taccess:public"+            Just AccessPrivate -> "\taccess:private"+            Just AccessProtected -> "\taccess:protected"+    in tagName tag ++ "\t" +++            tagFile tag ++ "\t" +++            tagPattern tag ++ ";\"\t" +++            tagKindLetter (tagKind tag) : "\t" +++            "line:" ++ show (tagLine tag) +++            parentStr +++            signatureStr +++            accessStr++type FileLines = Vector String+type TagC = FileLines -> Tag++createTags :: (Module SrcSpanInfo, FileLines) -> [Tag]+createTags (Module _ mbHead _ imports decls, fileLines) =+    let moduleTags = map tagC (maybe [] createModuleTags mbHead)+        importTags = map (tagC . createImportTag) imports+        declsTags = map tagC (concatMap createDeclTags decls)+        exportTags = filter ((==TExport) . tagKind) moduleTags+    -- TODO If there is no ModuleHead then apply public access modifier to all+    -- declarations tags+    in moduleTags ++ importTags ++ (applyAccessModifiers exportTags declsTags)+    where+        tagC :: TagC -> Tag+        tagC = ($ fileLines)+createTags _ = error "TODO Module is XmlPage/XmlHybrid (!)"++-- Apply a public access modifier to all declarations that are listed in the+-- module export specification+applyAccessModifiers :: [Tag] -> [Tag] -> [Tag]+applyAccessModifiers exportTags declTags = map applySingle declTags+    where+        applySingle :: Tag -> Tag+        applySingle tag =+            let name = tagName tag+                exported = any ((==name) . tagName) exportTags+            in if exported+                then tag { tagAccess = Just AccessPublic }+                else tag++createTag :: String -> TagKind -> Maybe (TagKind, String) -> Maybe String -> Maybe TagAccess -> SrcSpanInfo -> TagC+createTag name kind parent signature access (SrcSpanInfo (SrcSpan file line _ _ _) _) fileLines = Tag+    { tagName = name+    , tagFile = file+    -- TODO This probably needs to be escaped:+    , tagPattern = "/^" ++ (fileLines ! (line - 1)) ++ "$/"+    , tagKind = kind+    , tagLine = line+    , tagParent = parent+    , tagSignature = signature+    , tagAccess = access+    }++createModuleTags :: ModuleHead SrcSpanInfo -> [TagC]+createModuleTags (ModuleHead _ (ModuleName moduleLoc moduleName) _ mbExportSpecList) =+    case mbExportSpecList of+        Nothing -> [moduleTag]+        Just (ExportSpecList _ exports) ->+            moduleTag : map createExportTag exports+    where+        moduleTag = createTag moduleName TModule Nothing Nothing Nothing moduleLoc+        createExportTag :: ExportSpec SrcSpanInfo -> TagC+        createExportTag exportSpec =+            let (name, loc) = extractExportSpec exportSpec+            in createTag name TExport Nothing Nothing Nothing loc++createImportTag :: ImportDecl SrcSpanInfo -> TagC+createImportTag (ImportDecl loc (ModuleName _ name) qualified _ _ mbAlias mbSpecs) =+    let signature = case mbAlias of+            Nothing -> Nothing+            Just (ModuleName _ alias) -> Just alias+        access = case mbSpecs of+            Just (ImportSpecList _ False _) ->+                if qualified then Just AccessProtected else Nothing+            Just (ImportSpecList _ True _) ->+                -- imported names are excluded by 'hiding'+                if qualified then Just AccessProtected else Just AccessPrivate+            Nothing -> if qualified then Just AccessProtected else Just AccessPublic+    in createTag name TImport Nothing signature access loc++createDeclTags :: Decl SrcSpanInfo -> [TagC]+createDeclTags (TypeDecl _ hd _) =+    let (name, loc) = extractDeclHead hd+    in [createTag name TType Nothing Nothing Nothing loc]+createDeclTags (DataDecl _ dataOrNew _ hd constructors _) =+    let (name, loc) = extractDeclHead hd+        kind = case dataOrNew of+            DataType _ -> TData+            NewType _ -> TNewType+        dataTag = createTag name kind Nothing Nothing Nothing loc+    in dataTag : map (createConstructorTag (kind, name)) constructors+createDeclTags (TypeSig _ names t) =+    map createFunctionTag names+    where+        sig = prettyPrintStyleMode (Style OneLineMode 0 0) defaultMode t+        createFunctionTag :: Name SrcSpanInfo -> TagC+        createFunctionTag name =+            let (n, loc) = extractName name+            in createTag n TFunction Nothing (Just sig) Nothing loc+createDeclTags _ = []++-- TODO Also create tags for record fields+createConstructorTag :: (TagKind, String) -> QualConDecl SrcSpanInfo -> TagC+createConstructorTag parent (QualConDecl _ _ _ con) =+    let (name, loc) = extractConDecl con+    in createTag name TConstructor (Just parent) Nothing Nothing loc++extractExportSpec :: ExportSpec SrcSpanInfo -> (String, SrcSpanInfo)+extractExportSpec (EVar _ name) = extractQName name+extractExportSpec (EAbs _ name) = extractQName name+extractExportSpec (EThingAll _ name) = extractQName name+extractExportSpec (EThingWith _ name _) = extractQName name+extractExportSpec (EModuleContents _ (ModuleName loc name)) = (name, loc)++extractDeclHead :: DeclHead SrcSpanInfo -> (String, SrcSpanInfo)+extractDeclHead (DHead _ name _) = extractName name+extractDeclHead (DHInfix _ _ name _) = extractName name+extractDeclHead (DHParen _ hd') = extractDeclHead hd'++extractConDecl :: ConDecl SrcSpanInfo -> (String, SrcSpanInfo)+extractConDecl (ConDecl _ name _) = extractName name+extractConDecl (InfixConDecl _ _ name _) = extractName name+extractConDecl (RecDecl _ name _) = extractName name++extractName :: Name SrcSpanInfo -> (String, SrcSpanInfo)+extractName (Ident loc name) = (name, loc)+extractName (Symbol loc name) = (name, loc)++extractQName :: QName SrcSpanInfo -> (String, SrcSpanInfo)+extractQName (Qual loc (ModuleName _ moduleName) name) =+    (moduleName ++ "." ++ fst (extractName name), loc)+extractQName (UnQual loc name) = (fst (extractName name), loc)+extractQName (Special loc _) = ("_special_", loc)  -- TODO
+ util/tagbar-haskell.vim view
@@ -0,0 +1,42 @@+" This is a Haskell configuration for the Vim Tagbar plugin that uses+" lushtags.+"+" Tagbar can be found at:+" http://www.vim.org/scripts/script.php?script_id=3465+" http://majutsushi.github.com/tagbar/+"+" Paste this in to your vimrc file+" OR copy this file into your .vim/plugin directory+" OR load it from your vimrc file by adding a line like:+"+" source /path/to/tagbar-haskell.vim++if executable('lushtags')+    let g:tagbar_type_haskell = {+        \ 'ctagsbin' : 'lushtags',+        \ 'ctagsargs' : '--ignore-parse-error --',+        \ 'kinds' : [+            \ 'm:module:0',+            \ 'e:exports:1',+            \ 'i:imports:1',+            \ 't:declarations:0',+            \ 'd:declarations:1',+            \ 'n:declarations:1',+            \ 'f:functions:0',+            \ 'c:constructors:0'+        \ ],+        \ 'sro' : '.',+        \ 'kind2scope' : {+            \ 'd' : 'data',+            \ 'n' : 'newtype',+            \ 'c' : 'constructor',+            \ 't' : 'type'+        \ },+        \ 'scope2kind' : {+            \ 'data' : 'd',+            \ 'newtype' : 'n',+            \ 'constructor' : 'c',+            \ 'type' : 't'+        \ }+    \ }+endif