├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── Setup.hs ├── app ├── Indexer.hs ├── Server.hs └── Store.hs ├── cabal.project ├── cabal.project.freeze ├── haskell-code-explorer.cabal ├── install.hs ├── javascript ├── .ember-cli ├── .eslintrc.js ├── .gitignore ├── .watchmanconfig ├── README.md ├── app │ ├── app.js │ ├── components │ │ ├── .gitkeep │ │ ├── bottom-panel.js │ │ ├── expression-info.js │ │ ├── file-tree.js │ │ ├── haskell-module.js │ │ ├── identifier-info.js │ │ ├── identifier-name.js │ │ ├── infinite-list.js │ │ ├── info-window.js │ │ ├── input-with-autocomplete.js │ │ ├── instance-info.js │ │ ├── paginated-list.js │ │ ├── resizable-panel.js │ │ ├── text-file.js │ │ ├── type-component.js │ │ ├── type-signature-text.js │ │ └── type-signature.js │ ├── controllers │ │ ├── application.js │ │ ├── package.js │ │ ├── package │ │ │ ├── index.js │ │ │ ├── search.js │ │ │ └── show │ │ │ │ └── file.js │ │ ├── packages.js │ │ └── search.js │ ├── helpers │ │ └── .gitkeep │ ├── index.html │ ├── router.js │ ├── routes │ │ ├── application.js │ │ ├── package.js │ │ ├── package │ │ │ ├── index.js │ │ │ ├── search.js │ │ │ └── show │ │ │ │ ├── file.js │ │ │ │ └── index.js │ │ ├── packages.js │ │ └── search.js │ ├── services │ │ ├── settings.js │ │ └── store.js │ ├── styles │ │ └── app.scss │ ├── templates │ │ ├── application.hbs │ │ ├── bad-url.hbs │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── bottom-panel.hbs │ │ │ ├── expression-info.hbs │ │ │ ├── file-tree.hbs │ │ │ ├── haskell-module.hbs │ │ │ ├── identifier-info.hbs │ │ │ ├── identifier-name.hbs │ │ │ ├── infinite-list.hbs │ │ │ ├── info-window.hbs │ │ │ ├── input-with-autocomplete.hbs │ │ │ ├── instance-info.hbs │ │ │ ├── paginated-list.hbs │ │ │ ├── resizable-panel.hbs │ │ │ ├── text-file.hbs │ │ │ ├── type-component.hbs │ │ │ ├── type-signature-text.hbs │ │ │ └── type-signature.hbs │ │ ├── package.hbs │ │ ├── package │ │ │ ├── index.hbs │ │ │ ├── search.hbs │ │ │ ├── show.hbs │ │ │ └── show │ │ │ │ └── file.hbs │ │ ├── packages.hbs │ │ └── search.hbs │ └── utils │ │ ├── api-urls.js │ │ ├── color-themes.js │ │ ├── go-to-definition.js │ │ ├── language-extensions.js │ │ └── line-selection.js ├── bower.json ├── config │ ├── environment.js │ └── targets.js ├── ember-cli-build.js ├── package.json ├── public │ ├── assets │ │ ├── 32px.png │ │ ├── 40px.png │ │ ├── favicon.ico │ │ ├── haskell.ico │ │ └── throbber.gif │ └── robots.txt ├── release │ ├── assets │ │ ├── 32px-eebaf260766f5e0e773f53d3ea4f3e4d.png │ │ ├── 40px-51286e68b083696edaf4f9fc577e2a2d.png │ │ ├── favicon.ico │ │ ├── haskell-code-explorer-3325784c864ca6662b97be678eeea861.js │ │ ├── haskell-code-explorer-717832df8786392c9b8a9bd8010463d2.css │ │ ├── haskell.ico │ │ ├── throbber-62be6ed2b189444b472b8000dc187240.gif │ │ ├── vendor-b97a1e7d03d00bb4514dc45da44b51ff.js │ │ └── vendor-c68699fa3ef851ebff1b584737d944c2.css │ ├── index.html │ └── robots.txt ├── server │ ├── .eslintrc.js │ ├── index.js │ └── mocks │ │ └── packages.js ├── testem.js ├── tests │ ├── .eslintrc.js │ ├── acceptance │ │ ├── haskell-module-test.js │ │ ├── package-test.js │ │ └── packages-test.js │ ├── helpers │ │ └── .gitkeep │ ├── index.html │ ├── integration │ │ ├── .gitkeep │ │ └── components │ │ │ └── .gitkeep │ └── test-helper.js └── vendor │ ├── .gitkeep │ └── jquery-ui-1.12.1.custom │ ├── AUTHORS.txt │ ├── LICENSE.txt │ ├── external │ └── jquery │ │ └── jquery.js │ ├── images │ ├── ui-icons_444444_256x240.png │ ├── ui-icons_555555_256x240.png │ ├── ui-icons_777620_256x240.png │ ├── ui-icons_777777_256x240.png │ ├── ui-icons_cc0000_256x240.png │ └── ui-icons_ffffff_256x240.png │ ├── index.html │ ├── jquery-ui.css │ ├── jquery-ui.js │ ├── jquery-ui.min.css │ ├── jquery-ui.min.js │ ├── jquery-ui.structure.css │ ├── jquery-ui.structure.min.css │ ├── jquery-ui.theme.css │ ├── jquery-ui.theme.min.css │ └── package.json ├── src └── HaskellCodeExplorer │ ├── AST │ ├── RenamedSource.hs │ └── TypecheckedSource.hs │ ├── GhcUtils.hs │ ├── ModuleInfo.hs │ ├── PackageInfo.hs │ ├── Preprocessor.hs │ └── Types.hs ├── stack-8.0.2.yaml ├── stack-8.2.2.yaml ├── stack-8.4.3.yaml ├── stack-8.4.4.yaml ├── stack-8.6.3.yaml ├── stack-8.6.4.yaml ├── stack-8.6.5.yaml ├── stack-8.8.3.yaml ├── stack.yaml ├── test ├── Main.hs ├── data │ ├── File.hs │ ├── File1.hs │ ├── File2.hs │ ├── File3.hs │ └── FileAfterPreprocessor.hs └── test-package │ ├── .gitignore │ ├── Setup.hs │ ├── app │ └── Main.hs │ ├── src │ ├── Lib.hs │ └── Types.hs │ ├── stack-8.0.2.yaml │ ├── stack-8.2.2.yaml │ ├── stack-8.4.3.yaml │ ├── stack-8.4.4.yaml │ ├── stack-8.6.3.yaml │ ├── stack-8.6.4.yaml │ ├── stack-8.6.5.yaml │ ├── stack.yaml │ ├── test-package.cabal │ └── test │ └── Spec.hs └── vendor └── cabal-helper-0.8.1.2 ├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── LICENSE ├── README.md ├── Setup.hs ├── cabal-helper.cabal ├── cabal.project ├── lib └── Distribution │ └── Helper.hs ├── scripts ├── bump.sh ├── ci │ ├── build.sh │ ├── print-packages.sh │ ├── retry.sh │ └── steps │ │ ├── 00-config.sh │ │ ├── 05-print-packages.sh │ │ ├── 10-dependencies.sh │ │ ├── 15-print-packages.sh │ │ ├── 20-sdist.sh │ │ ├── 30-build.sh │ │ └── 40-test.sh └── update-cabal-common-section-instantiations.awk ├── src └── CabalHelper │ ├── Compiletime │ ├── Compat │ │ ├── Environment.hs │ │ ├── ProgramDb.hs │ │ └── Version.hs │ ├── Compile.hs │ ├── Data.hs │ ├── Log.hs │ ├── Types.hs │ └── Wrapper.hs │ ├── Runtime │ └── Main.hs │ └── Shared │ ├── Common.hs │ ├── InterfaceTypes.hs │ └── Sandbox.hs └── tests ├── CompileTest.hs ├── GhcSession.hs ├── bkpregex ├── Setup.hs ├── bkpregex.cabal ├── regex-example │ └── Main.hs ├── regex-indef │ ├── Regex.hs │ └── Str.hsig ├── regex-types │ └── Regex │ │ └── Types.hs └── str-impls │ └── Str │ ├── ByteString.hs │ └── String.hs ├── exeintlib ├── Exe.hs ├── Setup.hs ├── exeintlib.cabal ├── intlib │ └── IntLib.hs └── lib │ └── Lib.hs ├── exelib ├── Exe.hs ├── Setup.hs ├── exelib.cabal └── lib │ └── Lib.hs └── fliblib ├── FLib.hs ├── Setup.hs ├── fliblib.cabal └── lib └── Lib.hs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | docker: 3 | - image: fpco/stack-build:lts 4 | steps: 5 | - checkout 6 | - run: 7 | name: Copy provided stack.yaml to stack-build.txt 8 | command: cp ${STACK_FILE} stack-build.txt 9 | - restore_cache: 10 | name: Restore Cached Dependencies 11 | keys: 12 | - haskell-code-explorer-v1-{{ checksum "stack-build.txt" }} 13 | - run: 14 | name: Resolve/Update Dependencies 15 | command: stack --stack-yaml=${STACK_FILE} setup 16 | - run: 17 | name: Build 18 | command: stack --stack-yaml=${STACK_FILE} -j 2 build --test --no-run-tests 19 | - run: 20 | name: Install cabal-install 21 | command: stack --stack-yaml=${STACK_FILE} -j 2 install cabal-install 22 | - run: 23 | name: Update list of packages 24 | command: stack --stack-yaml=${STACK_FILE} exec --no-ghc-package-path cabal -- update 25 | - run: 26 | name: Run tests 27 | command: stack --stack-yaml=${STACK_FILE} test || stack --stack-yaml=${STACK_FILE} exec --no-ghc-package-path $(stack --stack-yaml=${STACK_FILE} path --dist-dir)/build/test/test 28 | - save_cache: 29 | name: Cache Dependencies 30 | key: haskell-code-explorer-v1-{{ checksum "stack-build.txt" }} 31 | paths: 32 | - "/root/.stack" 33 | - "/root/.cache" 34 | - "/root/.cabal" 35 | - ".stack-work" 36 | 37 | version: 2 38 | jobs: 39 | ghc-8.0.2: 40 | environment: 41 | - STACK_FILE: "stack-8.0.2.yaml" 42 | <<: *defaults 43 | 44 | ghc-8.2.2: 45 | environment: 46 | - STACK_FILE: "stack-8.2.2.yaml" 47 | <<: *defaults 48 | 49 | ghc-8.4.3: 50 | environment: 51 | - STACK_FILE: "stack-8.4.3.yaml" 52 | <<: *defaults 53 | 54 | ghc-8.4.4: 55 | environment: 56 | - STACK_FILE: "stack-8.4.4.yaml" 57 | <<: *defaults 58 | 59 | ghc-8.6.3: 60 | environment: 61 | - STACK_FILE: "stack-8.6.3.yaml" 62 | <<: *defaults 63 | 64 | ghc-8.6.4: 65 | environment: 66 | - STACK_FILE: "stack-8.6.4.yaml" 67 | <<: *defaults 68 | 69 | ghc-8.6.5: 70 | environment: 71 | - STACK_FILE: "stack-8.6.5.yaml" 72 | <<: *defaults 73 | 74 | workflows: 75 | version: 2 76 | build_and_test: 77 | jobs: 78 | - ghc-8.0.2 79 | - ghc-8.2.2 80 | - ghc-8.4.3 81 | - ghc-8.4.4 82 | - ghc-8.6.3 83 | - ghc-8.6.4 84 | - ghc-8.6.5 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | .stack-work/ 4 | .haskell-code-explorer/ 5 | .cabal-sandbox/ 6 | cabal.sandbox.config 7 | cabal.config 8 | log/ 9 | tmp/ 10 | TAGS 11 | \#*\# 12 | .\#* 13 | # stack 2.1 stack.yaml lock files 14 | stack*.yaml.lock 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Alexey Kiryushin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /app/Store.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE TypeApplications #-} 4 | {-# LANGUAGE TypeFamilyDependencies #-} 5 | {-# LANGUAGE TypeFamilies #-} 6 | {-# LANGUAGE FlexibleContexts #-} 7 | {-# LANGUAGE FlexibleInstances #-} 8 | {-# LANGUAGE TypeSynonymInstances #-} 9 | {-# LANGUAGE ScopedTypeVariables #-} 10 | {-# LANGUAGE DeriveGeneric #-} 11 | {-# LANGUAGE DeriveAnyClass #-} 12 | {-# LANGUAGE StrictData #-} 13 | {-# OPTIONS_GHC -Wno-orphans #-} 14 | 15 | -- | Read-only on-disk key-value store 16 | 17 | module Store where 18 | 19 | import Control.DeepSeq (NFData) 20 | import Control.Monad.IO.Class (liftIO) 21 | import qualified Control.Monad.State.Strict as S 22 | import qualified Data.ByteString as BS 23 | import qualified Data.ByteString.Short as BSS 24 | import Data.Either (Either) 25 | import qualified Data.Map.Strict as M 26 | import Data.Serialize ( 27 | Serialize, 28 | decode, 29 | encode, 30 | #if MIN_VERSION_cereal(0,5,8) 31 | #else 32 | get, put 33 | #endif 34 | ) 35 | import GHC.Generics (Generic) 36 | import Prelude hiding (lookup) 37 | import System.Directory (doesFileExist) 38 | import System.FilePath (()) 39 | import System.IO (Handle, IOMode(..), hTell, withFile) 40 | import System.IO.MMap (mmapFileByteString) 41 | 42 | data Store = Store 43 | { index :: M.Map BSS.ShortByteString Location 44 | , values :: BS.ByteString 45 | } 46 | 47 | data Location = Location 48 | { offset :: Int 49 | , length :: Int 50 | } deriving (Show, Eq, Ord, Generic, NFData) 51 | 52 | instance Serialize Location 53 | 54 | #if MIN_VERSION_cereal(0,5,8) 55 | #else 56 | instance Serialize BSS.ShortByteString where 57 | put = put . BSS.fromShort 58 | get = BSS.toShort <$> get 59 | #endif 60 | 61 | class StoreItem item where 62 | toByteString :: item -> BS.ByteString 63 | fromByteString :: BS.ByteString -> Either String item 64 | type KeyArgs item = arg | arg -> item 65 | itemKey :: KeyArgs item -> BSS.ShortByteString 66 | 67 | indexFileName :: FilePath 68 | indexFileName = "index" 69 | 70 | valuesFileName :: FilePath 71 | valuesFileName = "values" 72 | 73 | data ReadMode 74 | = ReadEntireFile 75 | | MemoryMapFile 76 | deriving (Show, Eq) 77 | 78 | load :: FilePath -> ReadMode -> IO (Either String Store) 79 | load directoryPath readMode = do 80 | let valuesFilePath = directoryPath valuesFileName 81 | indexFilePath = directoryPath indexFileName 82 | (valuesFileExists, indexFileExists) <- 83 | (,) <$> doesFileExist indexFilePath <*> doesFileExist valuesFilePath 84 | case (valuesFileExists, indexFileExists) of 85 | (True, True) -> do 86 | indexFile <- BS.readFile indexFilePath 87 | valuesFile <- 88 | case readMode of 89 | ReadEntireFile -> BS.readFile valuesFilePath 90 | MemoryMapFile -> mmapFileByteString valuesFilePath Nothing 91 | let eitherIndex = decode @(M.Map BSS.ShortByteString Location) indexFile 92 | case eitherIndex of 93 | Right locMap -> return . Right $ Store {index = locMap, values = valuesFile} 94 | Left err -> return . Left $ "Error while decoding index : " ++ err 95 | (False, False) -> 96 | return . Left $ "Cannot find index and values in " ++ directoryPath 97 | (True, False) -> return . Left $ "Cannot find index in " ++ directoryPath 98 | (False, True) -> return . Left $ "Cannot find values in " ++ directoryPath 99 | 100 | lookup :: (StoreItem item) => KeyArgs item -> Store -> Either String item 101 | lookup keyArgs = lookupByteString (itemKey keyArgs) 102 | 103 | lookupByteString :: 104 | (StoreItem item) => BSS.ShortByteString -> Store -> Either String item 105 | lookupByteString key store = 106 | case M.lookup key (index store) of 107 | Just (Location off len) -> 108 | fromByteString . BS.take len . BS.drop off $ values store 109 | Nothing -> Left $ "Cannot find item with key " ++ show key 110 | 111 | data State = 112 | State (M.Map BSS.ShortByteString Location) 113 | Handle 114 | 115 | add :: (StoreItem item) => item -> KeyArgs item -> S.StateT State IO () 116 | add item keyArgs = do 117 | let bs = toByteString item 118 | len = BS.length bs 119 | State locMap handle <- S.get 120 | off <- liftIO $ fromIntegral <$> hTell handle 121 | _ <- liftIO $ BS.hPut handle bs 122 | S.put $ State (M.insert (itemKey keyArgs) (Location off len) locMap) handle 123 | 124 | createStore :: 125 | FilePath -> (Handle -> IO (M.Map BSS.ShortByteString Location)) -> IO () 126 | createStore directoryPath action = 127 | withFile (directoryPath valuesFileName) WriteMode $ \valuesHandle -> do 128 | locMap <- action valuesHandle 129 | BS.writeFile (directoryPath indexFileName) (encode locMap) 130 | 131 | writeValues :: 132 | Handle 133 | -> M.Map BSS.ShortByteString Location 134 | -> S.StateT State IO () 135 | -> IO (M.Map BSS.ShortByteString Location) 136 | writeValues handle locMap action = do 137 | State locMap' _ <- S.execStateT action (State locMap handle) 138 | return locMap' 139 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | -- These are project-fixed settings and should not contain developer specific settings. 2 | -- 3 | -- Developer specific settings can be configured in cabal.project.local (e.g. optimization level) 4 | -- either manually or through 'cabal new-configure'. 5 | -- 6 | -- Refer to the documentation: https://cabal.readthedocs.io/en/latest/nix-local-build.html#cabal-new-configure 7 | -- https://cabal.readthedocs.io/en/latest/nix-local-build.html#configuring-builds-with-cabal-project 8 | -- 9 | -- A common config might be: 10 | -- -- all local packages 11 | -- optimization: 0 12 | -- 13 | -- -- always build dependencies with highest optimization 14 | -- package * 15 | -- optimization: 2 16 | 17 | with-compiler: ghc-8.4.4 18 | 19 | packages: . 20 | vendor/cabal-helper-0.8.1.2 21 | 22 | -------------------------------------------------------------------------------- /haskell-code-explorer.cabal: -------------------------------------------------------------------------------- 1 | name: haskell-code-explorer 2 | version: 0.1.0.0 3 | synopsis: Web application for exploring and understanding Haskell codebases 4 | Category: GHC,Web 5 | description: Please see README.md 6 | license: MIT 7 | license-file: LICENSE 8 | author: Alexey Kiryushin 9 | maintainer: alexey.a.kiryushin@gmail.com 10 | build-type: Simple 11 | cabal-version: >=1.20 12 | 13 | library 14 | default-language: Haskell2010 15 | exposed-modules: HaskellCodeExplorer.PackageInfo 16 | , HaskellCodeExplorer.ModuleInfo 17 | , HaskellCodeExplorer.Types 18 | , HaskellCodeExplorer.Preprocessor 19 | , HaskellCodeExplorer.GhcUtils 20 | , HaskellCodeExplorer.AST.RenamedSource 21 | , HaskellCodeExplorer.AST.TypecheckedSource 22 | hs-source-dirs: src 23 | ghc-options: -Wall -O2 -funbox-strict-fields 24 | build-depends: IntervalMap 25 | , aeson 26 | , attoparsec 27 | , base 28 | , blaze-html 29 | , deepseq 30 | , bytestring 31 | , cabal-helper 32 | , cereal 33 | , containers 34 | , directory 35 | , directory-tree 36 | , filemanip 37 | , filepath 38 | , ghc 39 | , ghc-paths 40 | , hashable 41 | , haddock-library 42 | , mtl 43 | , process 44 | , syb 45 | , uniplate 46 | , text 47 | , unordered-containers 48 | , vector 49 | , fast-logger 50 | , monad-logger 51 | , extra 52 | 53 | executable haskell-code-indexer 54 | main-is: Indexer.hs 55 | ghc-options: -Wall -rtsopts -O2 -funbox-strict-fields 56 | other-modules: Paths_haskell_code_explorer 57 | hs-source-dirs: app 58 | build-depends: IntervalMap 59 | , aeson 60 | , base 61 | , bytestring 62 | , cereal 63 | , directory 64 | , filepath 65 | , optparse-applicative 66 | , text 67 | , time 68 | , unordered-containers 69 | , uri-encode 70 | , fast-logger 71 | , monad-logger 72 | , zlib 73 | , haskell-code-explorer 74 | default-language: Haskell2010 75 | 76 | executable haskell-code-server 77 | main-is: Server.hs 78 | ghc-options: -Wall -O2 -rtsopts -funbox-strict-fields -threaded 79 | hs-source-dirs: app,src 80 | other-modules: HaskellCodeExplorer.Types, Store 81 | build-depends: IntervalMap 82 | , aeson 83 | , base 84 | , bytestring 85 | , cereal 86 | , containers 87 | , deepseq 88 | , directory 89 | , filemanip 90 | , filepath 91 | , blaze-html 92 | , text 93 | , unordered-containers 94 | , hashable 95 | , vector 96 | , wai 97 | , syb 98 | , haddock-library 99 | , servant 100 | , servant-server 101 | , mime-types 102 | , mtl 103 | , wai-extra 104 | , wai-middleware-static 105 | , warp 106 | , http-types 107 | , http-api-data 108 | , fast-logger 109 | , monad-logger 110 | , optparse-applicative 111 | , data-default 112 | , pagination 113 | , file-embed 114 | , mmap 115 | , lens 116 | , uri-encode 117 | , lens-aeson 118 | , wreq 119 | if impl(ghc >= 8.4.3) 120 | build-depends: ghc-compact 121 | 122 | test-suite test 123 | default-language: Haskell2010 124 | type: exitcode-stdio-1.0 125 | ghc-options: -Wall 126 | hs-source-dirs: test 127 | main-is: Main.hs 128 | build-depends: IntervalMap 129 | , QuickCheck 130 | , attoparsec 131 | , base 132 | , bytestring 133 | , containers 134 | , directory 135 | , filepath 136 | , hspec 137 | , text 138 | , uniplate 139 | , unordered-containers 140 | , monad-logger 141 | , process 142 | , vector 143 | , syb 144 | , haskell-code-explorer 145 | -------------------------------------------------------------------------------- /install.hs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stack 2 | {- stack script 3 | --resolver lts-13.12 4 | --ghc-options -Wall 5 | --package bytestring,text,filepath,directory,optparse-applicative,typed-process 6 | -} 7 | 8 | {-# LANGUAGE CPP #-} 9 | -- = About 10 | -- 11 | -- Install multiple versions of haskell-code-indexer, each with the version of 12 | -- GHC it was built with appended to the executable name. 13 | -- 14 | -- = Original 15 | -- 16 | -- Modified from the BSD3 licensed script at: 17 | -- https://github.com/haskell/haskell-ide-engine/blob/ec5e34ca52d389b713df918f02ff63920aede4be/install.hs 18 | -- 19 | -- Thanks haskell-ide-engine folks! 20 | -- 21 | -- = Changes from the original 22 | -- 23 | -- + Switched from Shake to IO script 24 | -- + Added optparse-applicative 25 | -- + Switched to Stack only (PRs welcome to support other tools) 26 | module Main (main) where 27 | 28 | import Control.Monad 29 | import Data.ByteString (ByteString) 30 | import qualified Data.ByteString.Lazy as LBS 31 | import Data.Char (isSpace) 32 | import Data.Foldable 33 | import Data.List (dropWhileEnd) 34 | import qualified Data.Text as T 35 | import Data.Text.Encoding 36 | import System.FilePath ((<.>), ()) 37 | import Options.Applicative 38 | import System.Directory (copyFile, removeFile) 39 | import System.Process.Typed 40 | 41 | -- | Keep this in sync with the stack.yamls at the top level of the project. 42 | supportedGhcVersions :: [Version] 43 | supportedGhcVersions = 44 | map Version ["8.0.2", "8.2.2", "8.4.3", "8.4.4", "8.6.3", "8.6.4","8.6.5"] 45 | 46 | newtype Version = Version { unVersion :: String } deriving Eq 47 | 48 | -- * CLI args 49 | 50 | data Args = Args 51 | { argBuildVersions :: [Version] 52 | , argBuildServer :: Bool 53 | } 54 | 55 | cliArgs :: IO Args 56 | cliArgs = 57 | customExecParser (prefs showHelpOnError) argsParser 58 | 59 | argsParser :: ParserInfo Args 60 | argsParser = 61 | fmap defaultToAll $ 62 | info (helper <*> parser) (fullDesc <> progDesc desc) 63 | where 64 | parser :: Parser Args 65 | parser = 66 | Args 67 | <$> (some indexVersion <|> pure mempty) 68 | <*> switch 69 | ( long "server" 70 | <> help "Build haskell-code-server" 71 | ) 72 | 73 | indexVersion :: Parser Version 74 | indexVersion = 75 | argument (eitherReader checkVersion) 76 | ( metavar "INDEX_VERSION" 77 | <> help "haskell-code-indexer-X-Y-Z version to build" 78 | ) 79 | 80 | checkVersion :: String -> Either String Version 81 | checkVersion s = 82 | case find ((==) (Version s)) supportedGhcVersions of 83 | Nothing -> 84 | Left . unwords $ 85 | "Not a supported GHC version. Currently supported versions are:" 86 | : map unVersion supportedGhcVersions 87 | 88 | Just v -> 89 | Right v 90 | 91 | defaultToAll :: Args -> Args 92 | defaultToAll args = 93 | if argBuildVersions args == mempty && argBuildServer args == False 94 | then Args (reverse supportedGhcVersions) True -- reverse to build latest first 95 | else args 96 | 97 | desc :: String 98 | desc = 99 | "Install haskell-code-indexer executables with the GHC version they were" 100 | <> " compiled with appended to their name. Builds everything if you don't" 101 | <> " specify options. Note that if you already have an indexer executable" 102 | <> " without the GHC version appended in your Stack's local bin" 103 | <> " it will be deleted." 104 | 105 | -- * Build 106 | 107 | main :: IO () 108 | main = 109 | run =<< cliArgs 110 | 111 | run :: Args -> IO () 112 | run args = do 113 | putStrLn (startupNotice args) 114 | when (argBuildServer args) buildServer 115 | for_ (argBuildVersions args) buildVersion 116 | 117 | startupNotice :: Args -> String 118 | startupNotice args = 119 | unlines 120 | $ "Building:" 121 | : (if argBuildServer args 122 | then [" + haskell-code-explorer"] 123 | else mempty) 124 | <> map versionEntry (argBuildVersions args) 125 | where 126 | versionEntry :: Version -> String 127 | versionEntry v = 128 | " + haskell-code-indexer-" <> unVersion v 129 | 130 | buildServer :: IO () 131 | buildServer = 132 | void $ execStack ["build", "--copy-bins", "haskell-code-explorer:haskell-code-server"] 133 | 134 | buildVersion :: Version -> IO () 135 | buildVersion v = do 136 | execStackWithVersion_ v ["build", "--copy-bins", "haskell-code-explorer:haskell-code-indexer"] 137 | localBinDir <- getLocalBin 138 | 139 | let 140 | -- exe is "exe" on Windows and "" otherwise 141 | fromFile = localBinDir "haskell-code-indexer" <.> exe 142 | toFile = localBinDir "haskell-code-indexer-" ++ unVersion v <.> exe 143 | 144 | copyFile fromFile toFile 145 | removeFile fromFile 146 | 147 | exe :: String 148 | #if defined(mingw32_HOST_OS) 149 | exe = "exe" 150 | #else 151 | exe = "" 152 | #endif 153 | 154 | -- | E.g. @"/home/user/bin"@. 155 | getLocalBin :: IO FilePath 156 | getLocalBin = do 157 | stackLocalDir' <- decodeUtf8 <$> execStack ["path", "--stack-yaml=stack.yaml", "--local-bin"] 158 | pure $ trimEnd (T.unpack stackLocalDir') 159 | 160 | -- | Uses the stack.yaml for the given @Version@. 161 | execStackWithVersion_ :: Version -> [String] -> IO () 162 | execStackWithVersion_ v args = do 163 | let stackFile = "stack-" ++ unVersion v ++ ".yaml" 164 | void $ execStack (("--stack-yaml=" ++ stackFile) : args) 165 | 166 | execStack :: [String] -> IO ByteString 167 | execStack = 168 | fmap LBS.toStrict . readProcessStdout_ . proc "stack" 169 | 170 | trimEnd :: String -> String 171 | trimEnd = dropWhileEnd isSpace 172 | -------------------------------------------------------------------------------- /javascript/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": true 9 | } 10 | -------------------------------------------------------------------------------- /javascript/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2017, 5 | sourceType: 'module' 6 | }, 7 | extends: 'eslint:recommended', 8 | env: { 9 | browser: true 10 | }, 11 | rules: { 12 | 'no-console':0, 13 | "no-useless-escape": 0 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /javascript/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | 5 | # dependencies 6 | /node_modules 7 | /bower_components 8 | 9 | # misc 10 | /.sass-cache 11 | /connect.lock 12 | /coverage/* 13 | /libpeerconnection.log 14 | npm-debug.log 15 | testem.log 16 | -------------------------------------------------------------------------------- /javascript/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /javascript/README.md: -------------------------------------------------------------------------------- 1 | # haskell-code-explorer 2 | 3 | ## Prerequisites 4 | * [Node.js](https://nodejs.org/) (with NPM) 5 | * [Ember CLI](https://ember-cli.com/) 6 | * [bower](https://bower.io/) 7 | 8 | ## Installation 9 | * `cd haskell-code-explorer/javascript` 10 | * `npm install` 11 | * `bower install` 12 | 13 | ## Running / Development 14 | * Start haskell-code-server on port 8080 15 | * `ember server --port=4200 --proxy=http://localhost:8080` 16 | * Visit your app at [http://localhost:4200](http://localhost:4200). 17 | 18 | To test the app with real-world data : 19 | * ember server --proxy=https://haskell-code-explorer.mfix.io/ 20 | 21 | ## Running Tests 22 | * `ember server --port=4200` 23 | * Open [http://localhost:4200/tests](http://localhost:4200/tests) in a browser 24 | 25 | ## Building 26 | * `ember build --environment production` 27 | -------------------------------------------------------------------------------- /javascript/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | var App; 7 | 8 | App = Ember.Application.extend({ 9 | modulePrefix: config.modulePrefix, 10 | Resolver: Resolver 11 | }); 12 | 13 | loadInitializers(App, config.modulePrefix); 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /javascript/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/app/components/.gitkeep -------------------------------------------------------------------------------- /javascript/app/components/bottom-panel.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | function show(component) { 4 | const height = Math.floor(component.$containerElement.height() /2); 5 | component.$().css({ 6 | "display":"block", 7 | "top" : height+"px" 8 | }); 9 | component.$topPanelElement.css({ 10 | "height":height+"px" 11 | }); 12 | } 13 | 14 | function hide(component) { 15 | const height = Math.floor(component.$containerElement.height()/2); 16 | component.$().css({ 17 | "display":"none", 18 | "height":height+"px" 19 | }); 20 | component.$topPanelElement.css({ 21 | "height":"100%" 22 | }); 23 | } 24 | 25 | export default Ember.Component.extend({ 26 | classNames:["bottom-panel"], 27 | didInsertElement : function () { 28 | this._super(...arguments); 29 | this.$topPanelElement = Ember.$(this.get('topPanelElementId')); 30 | this.$containerElement = Ember.$(this.get('containerElementId')); 31 | Ember.run.next(this,() => { 32 | Ember.$(this.element).resizable({ 33 | handles:"n", 34 | maxHeight:700, 35 | minHeight:200, 36 | resize: (event,ui) => { 37 | Ember.run.next(this,() => { 38 | this.$topPanelElement.css({"height": this.$containerElement.height() - ui.size.height}); 39 | }); 40 | } 41 | }); 42 | }); 43 | }, 44 | visibilityObserver : Ember.observer('visible',function () { 45 | this.get('visible') ? show(this) : hide(this); 46 | }), 47 | actions : { 48 | close () { 49 | this.set('visible',false); 50 | } 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /javascript/app/components/expression-info.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Component.extend({ 3 | }); 4 | -------------------------------------------------------------------------------- /javascript/app/components/file-tree.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const directoryTreeToJsTree = function (packageId,directoryTree) { 4 | return directoryTree.contents.map((node) => { 5 | const jsTreeNode = {}; 6 | jsTreeNode.text = node.name; 7 | jsTreeNode.data = node; 8 | if(node.path) { 9 | jsTreeNode.id = node.path; 10 | jsTreeNode.a_attr = {href:"/package/" + packageId + "/show/" + node.path}; 11 | } 12 | if(node.tag === "Dir") { 13 | jsTreeNode.children = directoryTreeToJsTree(packageId,node); 14 | jsTreeNode.state = {"opened" : containsHaskellModule(node)}; 15 | } else { 16 | if(node.isHaskellModule) { 17 | jsTreeNode.icon = "/assets/haskell.ico"; 18 | jsTreeNode.isHaskellModule = true; 19 | } else { 20 | jsTreeNode.icon = "jstree-file"; 21 | jsTreeNode.isHaskellModule = false; 22 | } 23 | } 24 | return jsTreeNode; 25 | }); 26 | }; 27 | 28 | const containsHaskellModule = function(node) { 29 | return node.contents.some((n) => { 30 | if(n.tag === "File") { 31 | return n.isHaskellModule; 32 | } else { 33 | return containsHaskellModule(n); 34 | } 35 | }); 36 | } 37 | 38 | const fileExtension = function (filename) { 39 | const idx = filename.lastIndexOf('.'); 40 | return (idx < 1) ? "" : filename.substr(idx + 1); 41 | } 42 | 43 | export default Ember.Component.extend({ 44 | query: null, 45 | sortType: "alphabetical", 46 | sortTypeObserver : Ember.observer('sortType',function() { 47 | Ember.run.next(this,() => { 48 | this.jstree.refresh(); 49 | }); 50 | }), 51 | didInsertElement : function () { 52 | this._super(...arguments); 53 | const element = this.element.getElementsByClassName('file-tree')[0]; 54 | const component = this; 55 | 56 | const jstreeElement = Ember.$(element).jstree({ 57 | 'core' : { 58 | 'data' : directoryTreeToJsTree(this.get('packageId'),this.get('directoryTree')) 59 | }, 60 | "plugins" : [ 61 | "search", 62 | "sort" 63 | ], 64 | "search": { 65 | "case_insensitive": true, 66 | "show_only_matches" : true, 67 | "show_only_matches_children": true 68 | }, 69 | 'sort' : function (a,b) { 70 | const node1 = this.get_node(a).data; 71 | const node2 = this.get_node(b).data; 72 | if(component.get("sortType") === "alphabetical") { 73 | return node1.name.localeCompare(node2.name); 74 | } else { 75 | const extendedName1 = (node1.tag === "Dir" ? "0" : "1") + fileExtension(node1.name) + node1.name; 76 | const extendedName2 = (node2.tag === "Dir" ? "0" : "1") + fileExtension(node2.name) + node2.name; 77 | return extendedName1.localeCompare(extendedName2); 78 | } 79 | } 80 | }); 81 | 82 | jstreeElement.on("select_node.jstree",(event,data) => { 83 | const file = data.node.data; 84 | if(file.tag != "Dir") { 85 | this.sendAction('openFile',file.path); 86 | } 87 | }); 88 | 89 | const jstree = jstreeElement.jstree(true); 90 | 91 | if(this.get('currentFile')) { 92 | jstree.select_node(this.get('currentFile')); 93 | const node = jstree.get_node(this.get('currentFile'),true)[0]; 94 | if(node) { 95 | node.scrollIntoView(); 96 | } 97 | } 98 | this.jstree = jstree; 99 | }, 100 | currentFileObserver : Ember.observer('currentFile',function() { 101 | Ember.run.next(() => { 102 | this.jstree.deselect_all(); 103 | this.jstree.select_node(this.get('currentFile')); 104 | }); 105 | }), 106 | queryObserver : Ember.observer('query',function() { 107 | if(this.get('query')) { 108 | this.jstree.search(this.get('query')); 109 | } else { 110 | this.jstree.clear_search(); 111 | } 112 | }), 113 | actions : { 114 | hide() { 115 | this.get('hide')(); 116 | } 117 | } 118 | }); 119 | -------------------------------------------------------------------------------- /javascript/app/components/identifier-info.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | 4 | export default Ember.Component.extend({ 5 | store : Ember.inject.service('store'), 6 | downloadedDocumentation : null, 7 | didInsertElement () { 8 | const onmouseup = (event) => { 9 | if(event.target.dataset.location) { 10 | let location; 11 | try { 12 | location = JSON.parse(event.target.dataset.location); 13 | } catch (e) { 14 | console.log(e); 15 | } 16 | if(location) { 17 | goToDefinition(this.get('store'),location,event.which,this.get('currentLineNumber')); 18 | } 19 | } 20 | }; 21 | this.element.addEventListener('mouseup',onmouseup); 22 | this._onmouseup = onmouseup; 23 | }, 24 | willDestroyElement : function () { 25 | if(this._onmouseup) { 26 | this.element.removeEventListener('mouseup',this._onmouseup); 27 | } 28 | }, 29 | //Naughty record selectors : 30 | //https://github.com/ghc/ghc/blob/ced2cb5e8fbf4493488d1c336da7b00d174923ce/compiler/typecheck/TcTyDecls.hs#L940-L961 31 | isNaughtyRecSel : Ember.computed('identifierInfo',function () { 32 | const idInfo = this.get('identifierInfo'); 33 | return idInfo ? (idInfo.details === "RecSelIdNaughty") : false; 34 | }), 35 | isExternalIdentifier : Ember.computed('identifierInfo',function () { 36 | const idInfo = this.get('identifierInfo'); 37 | return idInfo ? (idInfo.sort === "External") : false; 38 | }), 39 | identifierObserver : Ember.observer('identifierInfo',function () { 40 | this.set("downloadedDocumentation",""); 41 | const idInfo = this.get('identifierInfo'); 42 | if(idInfo) { 43 | const locationInfo = idInfo.locationInfo; 44 | if(locationInfo.tag === "ApproximateLocation") { 45 | const packageId = locationInfo.packageId.name + "-" + locationInfo.packageId.version; 46 | const currentIdentifier = idInfo; 47 | 48 | this.get('store').loadDefinitionSite(packageId, 49 | locationInfo.moduleName, 50 | locationInfo.componentId, 51 | locationInfo.entity, 52 | locationInfo.name) 53 | .then((definitionSite) => { 54 | Ember.run.next(this,() => { 55 | if(currentIdentifier === this.get('identifierInfo')) { 56 | this.set('downloadedDocumentation',definitionSite.documentation); 57 | }}) 58 | }).catch(() => { 59 | this.get('store').loadHoogleDocs(packageId, 60 | locationInfo.moduleName, 61 | locationInfo.entity, 62 | locationInfo.name) 63 | .then((hoogleDocs) => { 64 | Ember.run.next(this,() => { 65 | if(currentIdentifier === this.get('identifierInfo')) { 66 | this.set('downloadedDocumentation',hoogleDocs); 67 | }}); 68 | }); 69 | }); 70 | } 71 | } 72 | }) 73 | }); 74 | -------------------------------------------------------------------------------- /javascript/app/components/identifier-name.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | 4 | export default Ember.Component.extend({ 5 | store : Ember.inject.service('store'), 6 | name : Ember.computed('identifierElement',function() { 7 | const element = this.get('identifierElement'); 8 | if(element) { 9 | return element.innerText; 10 | } 11 | }), 12 | style : Ember.computed('identifierElement',function() { 13 | const element = this.get('identifierElement'); 14 | if(element) { 15 | return new Ember.String.htmlSafe("color:"+element.style.color); 16 | } 17 | }), 18 | locationInfo : Ember.computed('identifierInfo','identifierOccurrence',function() { 19 | if(this.get('identifierOccurrence.sort.tag') === "ModuleId") { 20 | return this.get('identifierOccurrence.sort.contents'); 21 | } else { 22 | return this.get('identifierInfo.locationInfo'); 23 | } 24 | }), 25 | location : Ember.computed('locationInfo',function() { 26 | const loc = this.get('locationInfo'); 27 | if(loc) { 28 | if(loc.tag === "ExactLocation") { 29 | return loc.modulePath; 30 | } else if(loc.tag === "ApproximateLocation") { 31 | if(loc.entity === "Mod") { 32 | return loc.packageId.name + "-" + loc.packageId.version; 33 | } else { 34 | return loc.packageId.name + "-" + loc.packageId.version + " " + loc.moduleName; 35 | } 36 | } else { 37 | return loc.contents; 38 | } 39 | } else { 40 | return ""; 41 | } 42 | }), 43 | isExternalIdentifier : Ember.computed('identifierInfo',function () { 44 | return (this.get('identifierInfo.sort') === "External"); 45 | }), 46 | actions : { 47 | goToDefinition (event) { 48 | goToDefinition(this.get('store'), 49 | this.get('locationInfo'), 50 | event.which, 51 | this.get('currentLineNumber')); 52 | return false; 53 | }, 54 | findReferences (identifierInfo,currentPackageId) { 55 | this.get('findReferences')(currentPackageId, 56 | identifierInfo.externalId, 57 | identifierInfo.demangledOccName, 58 | identifierInfo.locationInfo); 59 | } 60 | } 61 | }); 62 | -------------------------------------------------------------------------------- /javascript/app/components/infinite-list.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import { run } from '@ember/runloop'; 3 | import { observer } from '@ember/object'; 4 | 5 | let pageNumber; 6 | let updating = false; 7 | 8 | function initialize(component) { 9 | component.set('renderedElements',component.get('elements').slice(0,component.get('perPage'))); 10 | pageNumber = 1; 11 | } 12 | 13 | export default Component.extend({ 14 | renderedElements : [], 15 | init() { 16 | this._super(...arguments); 17 | initialize(this); 18 | }, 19 | elementsObserver : observer('elements',function() { 20 | initialize(this); 21 | const containerElement = document.getElementById(this.get('containerElementId')); 22 | if(containerElement) { 23 | containerElement.scrollTop = 0; 24 | } 25 | }), 26 | didInsertElement() { 27 | const containerElement = document.getElementById(this.get('containerElementId')); 28 | if(containerElement) { 29 | const component = this; 30 | containerElement.onscroll = function() { 31 | const perPage = component.get('perPage'); 32 | const elements = component.get('elements'); 33 | 34 | if(!updating && 35 | (pageNumber * perPage < elements.length) && 36 | (containerElement.scrollTop + containerElement.offsetHeight 37 | > component.element.offsetHeight - 100)) { 38 | 39 | updating = true; 40 | run.next(component,() => { 41 | const newElements = elements.slice(pageNumber * perPage,(pageNumber + 1) * perPage); 42 | component.get('renderedElements').pushObjects(newElements); 43 | pageNumber ++; 44 | updating = false; 45 | }); 46 | } 47 | } 48 | } 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /javascript/app/components/info-window.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | let resizing = false; 4 | let dragging = false; 5 | 6 | function updatePosition(component) { 7 | const targetElement = component.get('targetElement'); 8 | if(targetElement) { 9 | const infoWindowHeight = component.element.offsetHeight; 10 | const targetElementHeight = targetElement.offsetHeight; 11 | 12 | const parent = targetElement.parentNode;// element 13 | const containerElement = document.querySelector("#" + component.get('containerElementId')); 14 | 15 | //getBoundingClientRect() returns the smallest rectangle which contains 16 | //the entire element, with read-only left, top, right, bottom, x, y, width, 17 | //and height properties describing the overall border-box in pixels. Properties 18 | //other than width and height are relative to the top-left of the *viewport*. 19 | const targetTopViewport = targetElement.getBoundingClientRect().top; 20 | 21 | let containerTopViewport; 22 | if (containerElement) { 23 | containerTopViewport = containerElement.getBoundingClientRect().top; 24 | } else { 25 | containerTopViewport = 0; 26 | } 27 | 28 | let infoWindowTop; 29 | if(targetTopViewport < infoWindowHeight + containerTopViewport) { 30 | //offsetTop is the number of pixels from the top of the closest relatively 31 | //positioned parent element. 32 | infoWindowTop = targetElement.offsetTop + parent.offsetTop 33 | + targetElementHeight + 10 + "px"; 34 | } else { 35 | infoWindowTop = targetElement.offsetTop + parent.offsetTop 36 | - infoWindowHeight + "px"; 37 | } 38 | 39 | const infoWindowLeft = targetElement.offsetLeft + parent.offsetLeft + "px"; 40 | 41 | component.$().css({ 42 | top:infoWindowTop, 43 | left:infoWindowLeft 44 | }); 45 | } else { 46 | component.set('isPinned',false); 47 | } 48 | } 49 | 50 | export default Ember.Component.extend({ 51 | classNames : ["info-window-container"], 52 | attributeBindings: ['hidden'], 53 | isPinned : false, 54 | isFocused: false, 55 | didInsertElement () { 56 | const component = this; 57 | 58 | const $headerElement = Ember.$(component.element.querySelector(".info-window-header")); 59 | const $contentElement = Ember.$(component.element.querySelector(".info-window-content")); 60 | const $infoWindowElement = Ember.$(component.element.querySelector(".info-window")); 61 | const $infoWindowContainerElement = Ember.$(component.element); 62 | 63 | this.$headerElement = $headerElement; 64 | this.$contentElement = $contentElement; 65 | 66 | this.$().resizable({ 67 | handles: "n,w", 68 | minHeight: 80, 69 | minWidth: 400, 70 | start: function() { 71 | resizing = true; 72 | }, 73 | stop: function() { 74 | resizing = false; 75 | }, 76 | resize : function() { 77 | const containerHeight = $infoWindowContainerElement.height(); 78 | $infoWindowElement.css({ 79 | "height": containerHeight + 2 + "px" 80 | }); 81 | $contentElement.css({ 82 | "max-height":(containerHeight - $headerElement.outerHeight(true)) + "px" 83 | }); 84 | } 85 | }); 86 | this.$().draggable({ 87 | containment:"#" + this.get('containerElementId'), 88 | handle: $headerElement, 89 | start: function() { 90 | dragging = true; 91 | }, 92 | stop: function() { 93 | dragging = false; 94 | } 95 | }); 96 | }, 97 | mouseEnter () { 98 | if(!this.get('hasSelectedExpression')) { 99 | this.set('isFocused',true); 100 | } 101 | }, 102 | mouseLeave (event) { 103 | //Workaround for a bug in Chrome 104 | const element = document.elementFromPoint(event.clientX,event.clientY); 105 | if(element && element.classList.contains('link')) { 106 | return; 107 | } 108 | if(!resizing 109 | && !dragging 110 | && !this.get('isPinned') 111 | && !this.get('hasSelectedExpression')) { 112 | this.set('isFocused',false); 113 | } 114 | }, 115 | hidden : Ember.computed('isHoveredOverIdentifier', 116 | 'isFocused', 117 | 'hasSelectedExpression', 118 | 'isPinned', function() { 119 | if (this.$contentElement) { 120 | this.$contentElement.scrollTop(0); 121 | } 122 | if (this.get('isPinned') 123 | || this.get('isFocused') 124 | || this.get('isHoveredOverIdentifier') 125 | || this.get('hasSelectedExpression')) { 126 | return false; 127 | } else { 128 | return true; 129 | } 130 | }), 131 | didUpdate() { 132 | updatePosition(this); 133 | }, 134 | actions : { 135 | close() { 136 | this.set('isPinned',false); 137 | this.set('isFocused',false); 138 | this.set('hasSelectedExpression',false); 139 | }, 140 | pin() { 141 | this.toggleProperty('isPinned'); 142 | } 143 | } 144 | }); 145 | -------------------------------------------------------------------------------- /javascript/app/components/input-with-autocomplete.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Component.extend({ 3 | store : Ember.inject.service('store'), 4 | highlightedItemIndex: -1, 5 | items : [], 6 | query: null, 7 | didInsertElement() { 8 | const $input = Ember.$(this.element).find(".search-input"); 9 | const $autocompleteContainer = Ember.$(this.element).find(".autocomplete-container"); 10 | this.$input = $input; 11 | this.$autocompleteContainer = $autocompleteContainer; 12 | const width = $input.width() + 300; 13 | $autocompleteContainer.css({ 14 | "width" : width+"px", 15 | "top" : $input.outerHeight() 16 | }); 17 | $input.keyup((e) => { 18 | if(e.which === 13) { 19 | this.onEnter(); 20 | } else if(e.which === 27) { 21 | this.onEsc(); 22 | } else if(e.which === 40) { 23 | this.onDown(); 24 | } else if(e.which === 38) { 25 | this.onUp(); 26 | } 27 | }); 28 | $input.focusin(() => { 29 | this.showAutocompleteList(); 30 | }); 31 | $input.focusout(() => { 32 | //Timeout is needed to make sure that click event fires 33 | Ember.run.later((() => { 34 | this.hideAutocompleteList(); 35 | }), 100); 36 | }); 37 | }, 38 | willDestroyElement() { 39 | this._super(...arguments); 40 | this.$input.off('keyup'); 41 | this.$input.off('focusin'); 42 | this.$input.off('focusout'); 43 | }, 44 | onEnter() { 45 | if(this.get('highlightedItemIndex') !== -1) { 46 | const item = this.get('items')[this.get('highlightedItemIndex')]; 47 | if(item) { 48 | this.hideAutocompleteList(); 49 | this.get('selectItem')(item); 50 | } 51 | } else { 52 | this.hideAutocompleteList(); 53 | this.get('onSubmit')(this.get('query')); 54 | } 55 | }, 56 | onEsc() { 57 | this.hideAutocompleteList(); 58 | }, 59 | onDown() { 60 | this.showAutocompleteList(); 61 | const index = this.get('highlightedItemIndex'); 62 | const items = this.get('items'); 63 | const itemsCount = items.length; 64 | if(itemsCount > 0) { 65 | if(index !== -1) { 66 | if(index === itemsCount - 1) { 67 | this.set('highlightedItemIndex',0); 68 | } else { 69 | this.set('highlightedItemIndex',index+1); 70 | } 71 | } else { 72 | this.set('highlightedItemIndex',0); 73 | } 74 | } 75 | }, 76 | onUp() { 77 | this.showAutocompleteList(); 78 | const index = this.get('highlightedItemIndex'); 79 | const items = this.get('items'); 80 | const itemsCount = items.length; 81 | if(itemsCount > 0) { 82 | if(index !== -1) { 83 | if(index === 0) { 84 | this.set('highlightedItemIndex',itemsCount - 1); 85 | } else { 86 | this.set('highlightedItemIndex',index - 1); 87 | } 88 | } else { 89 | this.set('highlightedItemIndex',itemsCount - 1); 90 | } 91 | } 92 | }, 93 | hideAutocompleteList() { 94 | this.set('highlightedItemIndex',-1); 95 | this.$autocompleteContainer.css({ 96 | "display":"none", 97 | }); 98 | }, 99 | showAutocompleteList() { 100 | if(this.get('query') !== "") { 101 | this.$autocompleteContainer.css({ 102 | "display":"block" 103 | }); 104 | } 105 | }, 106 | searchUrlObserver : Ember.observer('createSearchUrlFunction',function() { 107 | this.notifyPropertyChange('query'); 108 | }), 109 | queryObserver : Ember.observer("query",function() { 110 | if(this.get('query')) { 111 | const perPage = this.get('maxItems') ? this.get('maxItems') : 10; 112 | const url = this.get('createSearchUrlFunction')(this.get('query')) + "?per_page=" + perPage; 113 | Ember.run.debounce(this, () => { 114 | this.get('store').loadFromUrlPaginated(url).then((result) => { 115 | Ember.run.next(() => { 116 | this.set('items',result.items); 117 | }); 118 | }); 119 | }, 400); 120 | this.showAutocompleteList(); 121 | } else { 122 | this.hideAutocompleteList(); 123 | this.set('items',[]); 124 | } 125 | }), 126 | actions : { 127 | onSubmit() { 128 | this.hideAutocompleteList(); 129 | this.get('onSubmit')(this.get('query')); 130 | }, 131 | goToDefinition (item) { 132 | this.hideAutocompleteList(); 133 | this.get('selectItem')(item); 134 | } 135 | } 136 | }); 137 | -------------------------------------------------------------------------------- /javascript/app/components/instance-info.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | 4 | export default Ember.Component.extend({ 5 | store : Ember.inject.service('store'), 6 | style : Ember.computed('nestedLevel',function() { 7 | return new Ember.String.htmlSafe("margin-left :" + this.get('nestedLevel') * 10 + "px"); 8 | }), 9 | nextNestedLevel : Ember.computed('nestedLevel',function () { 10 | return this.get('nestedLevel') + 1; 11 | }), 12 | actions : { 13 | goToDefinition (event) { 14 | goToDefinition(this.get('store'), 15 | this.get('instance.location'), 16 | event.which, 17 | this.get('currentLineNumber')); 18 | return false; 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /javascript/app/components/paginated-list.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | function loadItems(store,component,url) { 3 | store.loadFromUrlPaginated(url).then((result) => { 4 | Ember.run.next(() => { 5 | component.set('total',result.total); 6 | component.set('items',result.items); 7 | component.set('first',result.linkHeader.first); 8 | component.set('next',result.linkHeader.next); 9 | component.set('prev',result.linkHeader.prev); 10 | component.set('last',result.linkHeader.last); 11 | 12 | const pageMatch = url.match(/(&|\?)page=(\d+)/); 13 | const perPageMatch = url.match(/(&|\?)per_page=(\d+)/); 14 | 15 | const page = pageMatch ? pageMatch[2] : 1; 16 | const perPage = perPageMatch ? perPageMatch[2] : 20; 17 | 18 | if(result.linkHeader.next || result.linkHeader.prev) { 19 | component.set('firstItemOnPage',(page - 1) * perPage + 1); 20 | if(!result.linkHeader.last) { 21 | component.set('lastItemOnPage',result.total); 22 | } else { 23 | component.set('lastItemOnPage',page * perPage); 24 | } 25 | } 26 | }); 27 | }); 28 | } 29 | 30 | export default Ember.Component.extend({ 31 | store : Ember.inject.service('store'), 32 | init() { 33 | this._super(...arguments); 34 | if(this.get('url')) { 35 | loadItems(this.get('store'),this,this.get('url')); 36 | } 37 | }, 38 | urlObserver : Ember.observer('url',function () { 39 | loadItems(this.get('store'),this,this.get('url')); 40 | this.element.querySelector(".paginated-list-content").scrollTop = 0; 41 | }), 42 | actions : { 43 | update(url) { 44 | this.element.querySelector(".paginated-list-content").scrollTop = 0; 45 | loadItems(this.get('store'),this,url); 46 | } 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /javascript/app/components/resizable-panel.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | function hide (component,byUser) { 4 | component.$alsoResizeElement.css({left: 0}); 5 | component.$().css({width:0}); 6 | component.set('hidden',true); 7 | component.$(".show-left-panel-button").show(); 8 | if(byUser) { 9 | component.set('hiddenByUser',true); 10 | } 11 | } 12 | 13 | function show (component,byUser) { 14 | component.$alsoResizeElement.css({left: 300}); 15 | component.$().css({width:300}); 16 | component.set('hidden',false); 17 | component.$(".show-left-panel-button").hide(); 18 | if(byUser) { 19 | component.set('hiddenByUser',false); 20 | } 21 | } 22 | 23 | export default Ember.Component.extend({ 24 | hidden:false, 25 | hiddenByUser:false, 26 | didInsertElement : function () { 27 | this._super(...arguments); 28 | Ember.run.next(this,() => { 29 | const onresize = () => { 30 | if(!this.get('hiddenByUser')) { 31 | const width = window.innerWidth; 32 | if(!this.get('hidden') && width < 700) { 33 | hide(this,false); 34 | } else if(this.get('hidden') && width > 700) { 35 | show(this,false); 36 | } 37 | } 38 | }; 39 | this._onresize = onresize; 40 | window.addEventListener('resize', onresize); 41 | const $alsoResizeElement = Ember.$(this.get('alsoResizeElementId')); 42 | Ember.$(this.element).resizable({ 43 | maxWidth: 800, 44 | minWidth: 200, 45 | handles: 'e', 46 | resize: (event,ui) => { 47 | Ember.run.next(this,() => { 48 | $alsoResizeElement.css({left: ui.size.width}); 49 | }); 50 | } 51 | }); 52 | this.$alsoResizeElement = $alsoResizeElement; 53 | if(window.innerWidth < 700) { 54 | this.set('hidden',true); 55 | hide(this,false); 56 | } 57 | }); 58 | }, 59 | hideButtonLabel : Ember.computed('hidden',function() { 60 | return this.get('hidden') ? ">" : "<"; 61 | }), 62 | willDestroyElement() { 63 | if(this._onresize) { 64 | window.removeEventListener('resize',this._onresize); 65 | } 66 | }, 67 | actions : { 68 | hide() { 69 | if(this.get('hidden')) { 70 | show(this,true); 71 | } else { 72 | hide(this,true); 73 | } 74 | } 75 | } 76 | }); 77 | -------------------------------------------------------------------------------- /javascript/app/components/text-file.js: -------------------------------------------------------------------------------- 1 | /* global showdown */ 2 | import Ember from 'ember'; 3 | import {initializeLineSelection} from '../utils/line-selection'; 4 | 5 | function escapeHtml(text) { 6 | return text.replace(/[\"&<>]/g, function (a) { 7 | return { '"': '"', '&': '&', '<': '<', '>': '>' }[a]; 8 | }); 9 | } 10 | 11 | function addLineNumbers (text) { 12 | const start = ""; 13 | const end = "
"; 14 | let lineNumber = 0; 15 | const lines = text.split("\n").map((line) => { 16 | lineNumber ++; 17 | const lineNumberHtml = ""+lineNumber+""; 18 | const lineContentHtml = ""+escapeHtml(line)+""; 19 | return ""+ lineNumberHtml + lineContentHtml + ""; 20 | }).join(""); 21 | return start + lines + end; 22 | } 23 | 24 | const markdownExtensions = ["markdown", "mdown", "mkdn", "mkd", "md"]; 25 | 26 | export default Ember.Component.extend({ 27 | isMarkdown : Ember.computed('path',function() { 28 | const maybeExtension = this.get('path').split('.').pop(); 29 | return markdownExtensions.any((extension) => (maybeExtension === extension)); 30 | }), 31 | html : Ember.computed('path','isMarkdown',function() { 32 | if(this.get('isMarkdown')) { 33 | return this.markdownConverter.makeHtml(this.get('text')); 34 | } else { 35 | return addLineNumbers(this.get('text')); 36 | } 37 | }), 38 | init() { 39 | this._super(...arguments); 40 | this.markdownConverter = new showdown.Converter(); 41 | }, 42 | didInsertElement() { 43 | const sourceCodeContainerElement = this.element.querySelector('.source-code-container'); 44 | initializeLineSelection(sourceCodeContainerElement,this); 45 | this.element.parentNode.scrollTop = 0; 46 | }, 47 | willDestroyElement : function () { 48 | this.cleanup(); 49 | }, 50 | cleanup() { 51 | if(this._onhashchange) { 52 | window.removeEventListener('hashchange',this._onhashchange); 53 | } 54 | if(this._onkeydown) { 55 | document.removeEventListener('keydown',this._onkeydown); 56 | } 57 | if(this._onkeyup) { 58 | document.removeEventListener('keyup',this._onkeyup); 59 | } 60 | }, 61 | pathObserver : Ember.observer('path',function() { 62 | Ember.run.next(this,() => { 63 | this.cleanup(); 64 | this.didInsertElement(); 65 | }); 66 | }) 67 | }); 68 | -------------------------------------------------------------------------------- /javascript/app/components/type-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | 4 | export default Ember.Component.extend({ 5 | store : Ember.inject.service('store'), 6 | tagName : 'span', 7 | classNames: ["type-component"], 8 | contextMenu() {//right mouse button click to show kind of a type constructor or type variable 9 | if(this.get('identifiers') && this.get('internalId')) { 10 | this.set('expanded',true); 11 | } 12 | return false; 13 | }, 14 | linkClass : Ember.computed('identifierInfo',function() { 15 | return this.get('identifierInfo') ? "link" : ""; 16 | }), 17 | identifierInfo : Ember.computed('internalId',function() { 18 | return this.get('internalId') ? this.get('identifiers')[this.get('internalId')] : null; 19 | }), 20 | actions : { 21 | onmouseup (event) { 22 | if(this.get('identifierInfo') && (event.which !== 3 )) { 23 | const locationInfo = this.get('identifierInfo').locationInfo; 24 | goToDefinition(this.get('store'),locationInfo,event.which,this.get('currentLineNumber')); 25 | return false; 26 | } 27 | } 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /javascript/app/components/type-signature-text.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Component.extend({ 3 | tagName : "span" 4 | }); 5 | -------------------------------------------------------------------------------- /javascript/app/components/type-signature.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Component.extend({ 3 | tagName : "span", 4 | expandTypeSynonyms: false, 5 | expandTypeSynonymsLabel : Ember.computed('expandTypeSynonyms',function() { 6 | return this.get('expandTypeSynonyms') ? "Show type synonyms" : "Expand type synonyms"; 7 | }), 8 | components : Ember.computed('type','expandTypeSynonyms',function() { 9 | if(this.get('expandTypeSynonyms') && this.get('type.componentsExpanded')) { 10 | return this.get('type.componentsExpanded'); 11 | } else { 12 | return this.get('type.components'); 13 | } 14 | }), 15 | typeObserver : Ember.observer('type',function() { 16 | this.set('expandTypeSynonyms',false); 17 | }), 18 | actions : { 19 | toggleExpandTypeSynonyms () { 20 | this.toggleProperty('expandTypeSynonyms'); 21 | } 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /javascript/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import {updateColorThemeCss,themes} from '../utils/color-themes'; 2 | import Ember from 'ember'; 3 | 4 | export default Ember.Controller.extend({ 5 | settings : Ember.inject.service('settings'), 6 | themes: Object.values(themes), 7 | init() { 8 | this._super(...arguments); 9 | updateColorThemeCss(this.get('settings').get('colorTheme')); 10 | }, 11 | currentTheme: Ember.computed('settings',function() { 12 | return this.get('settings.colorTheme.id'); 13 | }), 14 | actions : { 15 | themeChanged (themeId) { 16 | const theme = themes[themeId]; 17 | this.get('settings').set('colorTheme',theme); 18 | updateColorThemeCss(theme); 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /javascript/app/controllers/package.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | import {urls} from '../utils/api-urls'; 4 | 5 | export default Ember.Controller.extend({ 6 | store : Ember.inject.service('store'), 7 | currentFile : null, 8 | loadItemsFunction : null, 9 | query : null, 10 | searchMode : "currentPackage", 11 | createSearchUrlFunction : Ember.computed("searchMode","model",function() { 12 | const packageId = this.get('model.id'); 13 | if(this.get('searchMode') === "currentPackage") { 14 | return (query) => urls.identifierSearchUrl(packageId,query); 15 | } else { 16 | return (query) => urls.globalIdentifiersUrl(query); 17 | } 18 | }), 19 | actions : { 20 | searchIdentifier (query) { 21 | if(query) { 22 | this.set('currentFile',null); 23 | document.title = this.get('model.id'); 24 | if(this.get('searchMode') === "currentPackage") { 25 | this.transitionToRoute('package.search',query); 26 | } else { 27 | this.transitionToRoute('search',query); 28 | } 29 | } 30 | }, 31 | showIdentifier (identifierInfo) { 32 | goToDefinition(this.get('store'), 33 | identifierInfo.locationInfo, 34 | 1,//left mouse button 35 | null); 36 | return false; 37 | } 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /javascript/app/controllers/package/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Controller.extend({ 3 | modulesFiltered : Ember.computed('model','query',function () { 4 | const query = this.get('query'); 5 | const modules = Object.keys(this.get('model.modules')).sort(); 6 | if(query) { 7 | const regExp = new RegExp(query,"i"); 8 | return modules.filter((p) => p.search(regExp) != -1); 9 | } else { 10 | return modules; 11 | } 12 | }) 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /javascript/app/controllers/package/search.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../../utils/go-to-definition'; 3 | 4 | export default Ember.Controller.extend({ 5 | store : Ember.inject.service('store'), 6 | actions : { 7 | goToDefinition (locationInfo,event) { 8 | goToDefinition(this.get('store'), 9 | locationInfo, 10 | event.which, 11 | null); 12 | return false; 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /javascript/app/controllers/package/show/file.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Controller.extend({ 3 | settings : Ember.inject.service('settings'), 4 | actions : { 5 | findReferences(packageId,externalId,occName,locationInfo) { 6 | this.send('updateReferences',packageId,externalId,occName,locationInfo); 7 | } 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /javascript/app/controllers/packages.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | 4 | export default Ember.Controller.extend({ 5 | store : Ember.inject.service('store'), 6 | queryObserver : Ember.observer("query",function() { 7 | Ember.run.debounce(this, () => { 8 | const regExp = new RegExp(this.get('query'),"i"); 9 | const packages = this.get('model').filter((p) => p.name.search(regExp) != -1); 10 | Ember.run.next(() => { 11 | this.set('packages',packages); 12 | }); 13 | }, 300); 14 | }), 15 | actions: { 16 | searchIdentifier (query) { 17 | if(query) { 18 | document.title = "Haskell code explorer"; 19 | this.transitionToRoute('search',query); 20 | } 21 | }, 22 | showIdentifier (identifierInfo) { 23 | goToDefinition(this.get('store'), 24 | identifierInfo.locationInfo, 25 | 1,//left mouse button 26 | null); 27 | return false; 28 | } 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /javascript/app/controllers/search.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {goToDefinition} from '../utils/go-to-definition'; 3 | 4 | export default Ember.Controller.extend({ 5 | store : Ember.inject.service('store'), 6 | actions : { 7 | goToDefinition (locationInfo,event) { 8 | goToDefinition(this.get('store'), 9 | locationInfo, 10 | event.which, 11 | null); 12 | return false; 13 | }, 14 | searchIdentifier (query) { 15 | if(query) { 16 | document.title = "Haskell code explorer"; 17 | this.transitionToRoute('search',query); 18 | } 19 | }, 20 | showIdentifier (identifierInfo) { 21 | goToDefinition(this.get('store'), 22 | identifierInfo.locationInfo, 23 | 1,//left mouse button 24 | null); 25 | return false; 26 | } 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /javascript/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/app/helpers/.gitkeep -------------------------------------------------------------------------------- /javascript/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Haskell Code Explorer 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | 12 | 13 | {{content-for 'head-footer'}} 14 | 15 | 16 | {{content-for 'body'}} 17 | 18 | 19 | {{content-for 'body-footer'}} 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /javascript/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | var Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | this.route('packages',{path:''}); 10 | this.route('search',{path:'/search/:query'}); 11 | this.route('package', {path:'/package/:packageId'}, function() { 12 | this.route('show',function() { 13 | this.route('file', {path:'*filePath'}, function() { 14 | }); 15 | }); 16 | this.route('search',{path:'/search/:query'}); 17 | }); 18 | this.route('bad-url', { path: '/*badurl' }); 19 | }); 20 | 21 | export default Router; 22 | -------------------------------------------------------------------------------- /javascript/app/routes/application.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | export default Ember.Route.extend({ 3 | }); 4 | -------------------------------------------------------------------------------- /javascript/app/routes/package.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {urls} from '../utils/api-urls'; 3 | 4 | export default Ember.Route.extend({ 5 | store : Ember.inject.service('store'), 6 | model (params) { 7 | return this.get('store').loadPackage(params.packageId) 8 | .catch((e) => {console.log(e);this.transitionTo("/package-not-found");}); 9 | }, 10 | setupController(controller, model) { 11 | this._super(controller, model); 12 | controller.set('bottomPanelVisible',false); 13 | }, 14 | actions : { 15 | openFile (filePath) { 16 | this.transitionTo('package.show.file',filePath); 17 | }, 18 | fileOpened (filePath) { 19 | if(this.get('controller')) { 20 | this.set('controller.currentFile',filePath); 21 | } 22 | }, 23 | updateReferences(packageId,externalId,occName,locationInfo,noScrollIntoView) { 24 | this.get('store').loadGlobalReferences(externalId).then((refs) => { 25 | Ember.run.next(this,() => { 26 | this.set('controller.globalReferences',refs); 27 | if(!noScrollIntoView) { 28 | Ember.run.schedule('afterRender', () => { 29 | const element = document.getElementById('references-package-'+packageId); 30 | if(element) { 31 | element.scrollIntoView(); 32 | } 33 | }); 34 | } 35 | }); 36 | }); 37 | this.set('controller.packageId',packageId); 38 | this.set('controller.externalId',externalId); 39 | this.set('controller.occName',occName); 40 | this.set('controller.locationInfo',locationInfo); 41 | this.set('controller.bottomPanelVisible',true); 42 | this.set('controller.referencesUrl',urls.referencesUrl(packageId,externalId)+"?per_page=50"); 43 | }, 44 | didTransition() { 45 | document.title = this.currentModel.id; 46 | return true; 47 | } 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /javascript/app/routes/package/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | afterModel : function (model,transition) { 5 | transition.send("fileOpened",null); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /javascript/app/routes/package/search.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {urls} from '../../utils/api-urls'; 3 | import {goToDefinition} from '../../utils/go-to-definition'; 4 | 5 | export default Ember.Route.extend({ 6 | store : Ember.inject.service('store'), 7 | model (params) { 8 | return { 9 | query: params.query, 10 | url: urls.identifierSearchUrl(this.modelFor('package').id,params.query)+"?per_page=20" 11 | }; 12 | }, 13 | afterModel () { 14 | const onmouseup = (event) => { 15 | // This makes links in documentation clickable 16 | if(event.target.dataset.location) { 17 | let location; 18 | try { 19 | location = JSON.parse(event.target.dataset.location); 20 | } catch (e) { 21 | console.log(e); 22 | } 23 | if(location) { 24 | goToDefinition(this.get('store'),location,event.which); 25 | } 26 | } 27 | }; 28 | this._onmouseup = onmouseup; 29 | document.addEventListener('mouseup',onmouseup); 30 | }, 31 | deactivate() { 32 | if(this._onmouseup) { 33 | document.removeEventListener('mouseup',this._onmouseup); 34 | } 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /javascript/app/routes/package/show/file.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | store : Ember.inject.service(), 5 | model : function (params) { 6 | const packageInfo = this.modelFor('package'); 7 | if(packageInfo.modules[params.filePath]) { 8 | return this.get('store').loadHaskellModule(packageInfo.id,params.filePath) 9 | .catch((e) => {console.log(e);this.transitionTo("/not-found");}); 10 | } else { 11 | return this.get('store').loadFile(packageInfo.id,params.filePath) 12 | .then((result) => { 13 | document.title = packageInfo.id; 14 | return result; 15 | }) 16 | .catch((e) => {console.log(e);this.transitionTo("/not-found");}); 17 | } 18 | }, 19 | afterModel (model) { 20 | document.title = model.id + " - " + this.modelFor('package').id; 21 | }, 22 | actions : { 23 | didTransition : function () { 24 | this.send("fileOpened",this.currentModel.id); 25 | } 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /javascript/app/routes/package/show/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | afterModel : function (model,transition) { 5 | transition.send("fileOpened",null); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /javascript/app/routes/packages.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {urls} from '../utils/api-urls'; 3 | import config from '../config/environment'; 4 | 5 | export default Ember.Route.extend({ 6 | model () { 7 | return Ember.$.getJSON(urls.packagesUrl); 8 | }, 9 | setupController(controller, model) { 10 | this._super(controller, model); 11 | controller.set('packages',model); 12 | controller.set('createSearchUrlFunction',(query) => { 13 | return urls.globalIdentifiersUrl(query); 14 | }); 15 | }, 16 | afterModel () { 17 | document.title = config.APP.title; 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /javascript/app/routes/search.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {urls} from '../utils/api-urls'; 3 | import {goToDefinition} from '../utils/go-to-definition'; 4 | 5 | export default Ember.Route.extend({ 6 | store : Ember.inject.service('store'), 7 | model (params) { 8 | return { 9 | query: params.query, 10 | url: urls.globalIdentifiersUrl(params.query)+"?per_page=20" 11 | }; 12 | }, 13 | setupController(controller, model) { 14 | this._super(controller, model); 15 | controller.set('createSearchUrlFunction',(query) => { 16 | return urls.globalIdentifiersUrl(query); 17 | }); 18 | }, 19 | afterModel () { 20 | const onmouseup = (event) => { 21 | // This makes links in documentation clickable 22 | if(event.target.dataset.location) { 23 | let location; 24 | try { 25 | location = JSON.parse(event.target.dataset.location); 26 | } catch (e) { 27 | console.log(e); 28 | } 29 | if(location) { 30 | goToDefinition(this.get('store'),location,event.which); 31 | } 32 | } 33 | }; 34 | this._onmouseup = onmouseup; 35 | document.addEventListener('mouseup',onmouseup); 36 | }, 37 | deactivate() { 38 | if(this._onmouseup) { 39 | document.removeEventListener('mouseup',this._onmouseup); 40 | } 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /javascript/app/services/settings.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import {themes} from '../utils/color-themes'; 3 | 4 | export default Ember.Service.extend({ 5 | init() { 6 | this._super(...arguments); 7 | if(localStorage) { 8 | const colorThemeId = localStorage.getItem("colorThemeId"); 9 | const colorTheme = themes[colorThemeId]; 10 | if(colorThemeId) { 11 | this.set('colorTheme',colorTheme); 12 | } 13 | } 14 | }, 15 | colorTheme : themes["darkTheme"], 16 | settingsObserver : Ember.observer("colorTheme",function() { 17 | if(localStorage) { 18 | localStorage.setItem("colorThemeId",this.get('colorTheme').id); 19 | } 20 | }) 21 | }); 22 | -------------------------------------------------------------------------------- /javascript/app/services/store.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import RSVP from 'rsvp'; 3 | import {urls} from '../utils/api-urls'; 4 | 5 | 6 | //******************************************************************************** 7 | //https://coderwall.com/p/zrlulq/parsing-a-link-header-in-javascript 8 | function unquote(value) { 9 | if (value.charAt(0) == '"' && value.charAt(value.length - 1) == '"') { 10 | return value.substring(1, value.length - 1); 11 | } else { 12 | return value; 13 | } 14 | } 15 | 16 | function parseLinkHeader(header) { 17 | if(!header) {return {}} 18 | var linkexp = /<[^>]*>\s*(\s*;\s*[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*")))*(,|$)/g; 19 | var paramexp = /[^\(\)<>@,;:"\/\[\]\?={} \t]+=(([^\(\)<>@,;:"\/\[\]\?={} \t]+)|("[^"]*"))/g; 20 | 21 | var matches = header.match(linkexp); 22 | var rels = new Object(); 23 | for (let i = 0; i < matches.length; i++) { 24 | var split = matches[i].split('>'); 25 | var href = split[0].substring(1); 26 | var ps = split[1]; 27 | var link = new Object(); 28 | link.href = href; 29 | var s = ps.match(paramexp); 30 | for (let j = 0; j < s.length; j++) { 31 | var p = s[j]; 32 | var paramsplit = p.split('='); 33 | var name = paramsplit[0]; 34 | link[name] = unquote(paramsplit[1]); 35 | } 36 | 37 | if (link.rel != undefined) { 38 | rels[link.rel] = link; 39 | } 40 | } 41 | return rels; 42 | } 43 | //******************************************************************************** 44 | 45 | 46 | export default Ember.Service.extend({ 47 | init() { 48 | this.packages = {}; 49 | this.files = {}; 50 | this.haskellModules = {}; 51 | this.definitionSites = {}; 52 | this.modulePaths = {}; 53 | this.expressions = {}; 54 | this.references = {}; 55 | this.globalReferences = {}; 56 | this.hoogleDocs = {}; 57 | }, 58 | loadPackage(packageId) { 59 | const packageInfo = this.packages[packageId]; 60 | if(packageInfo) { 61 | return new RSVP.Promise((resolve) => {resolve(packageInfo);}); 62 | } else { 63 | const url = urls.packageInfoUrl(packageId); 64 | return Ember.$.getJSON(url).then((packageInfo) => { 65 | this.packages[packageId] = packageInfo; 66 | return packageInfo; 67 | }); 68 | } 69 | }, 70 | loadFile(packageId,filePath) { 71 | const fileId = packageId + "/" + filePath; 72 | const file = this.files[fileId]; 73 | if(file) { 74 | return new RSVP.Promise((resolve) => {resolve(file);}); 75 | } else { 76 | const url = urls.fileUrl(packageId,filePath); 77 | return Ember.$.get({url:url,dataType:"text"}).then((text) => { 78 | const file = {}; 79 | file.text = text; 80 | file.packageId = packageId; 81 | file.isHaskellModule = false; 82 | file.id = filePath; 83 | this.files[fileId] = file; 84 | return file; 85 | }); 86 | } 87 | }, 88 | loadHaskellModule(packageId,filePath) { 89 | const moduleId = packageId + "/" + filePath ; 90 | const module = this.haskellModules[moduleId]; 91 | if(module) { 92 | return new RSVP.Promise((resolve)=>{resolve(module);}); 93 | } else { 94 | const url = urls.haskellModuleUrl(packageId,filePath); 95 | return Ember.$.getJSON(url).then((module) => { 96 | module.packageId = packageId; 97 | module.isHaskellModule = true; 98 | this.haskellModules[moduleId] = module; 99 | return module; 100 | }); 101 | } 102 | }, 103 | loadDefinitionSite(packageId,moduleName,componentId,entity,name) { 104 | const id = packageId + "/"+ componentId + "/" + moduleName + "/" + entity + "/" + name; 105 | const definitionSite = this.definitionSites[id]; 106 | if(definitionSite) { 107 | return new RSVP.Promise((resolve)=>{resolve(definitionSite);}); 108 | } else { 109 | const url = urls.identifierDefinitionSiteUrl(packageId,moduleName,componentId,entity,name); 110 | return Ember.$.getJSON(url).then((definitionSite) => { 111 | this.definitionSites[id] = definitionSite; 112 | return definitionSite; 113 | }); 114 | } 115 | }, 116 | loadExpressions(packageId,modulePath,lineStart,columnStart,lineEnd,columnEnd) { 117 | const id = packageId + "/" + encodeURIComponent(modulePath) 118 | + "/" + lineStart + "/" + columnStart + "/" + lineEnd + "/" + columnEnd; 119 | const exprs = this.expressions[id]; 120 | if(exprs) { 121 | return new RSVP.Promise((resolve)=>{resolve(exprs);}); 122 | } else { 123 | const url = urls.expressionsUrl(packageId,modulePath,lineStart,columnStart,lineEnd,columnEnd); 124 | return Ember.$.getJSON(url).then((exprs) => { 125 | this.expressions[id] = exprs; 126 | return exprs; 127 | }); 128 | } 129 | }, 130 | loadFromUrlPaginated(url) { 131 | return Ember.$.getJSON(url).then((items,textStatus,jqXHR) => { 132 | const linkHeaderText = jqXHR.getResponseHeader('Link'); 133 | const totalCountHeaderText = jqXHR.getResponseHeader('x-total-count'); 134 | const linkHeader = parseLinkHeader(linkHeaderText); 135 | const total = parseInt(totalCountHeaderText); 136 | return { 137 | items:items, 138 | total:total, 139 | linkHeader:linkHeader 140 | }; 141 | }); 142 | }, 143 | loadGlobalReferences(externalId) { 144 | const globalReferences = this.globalReferences[externalId]; 145 | if(globalReferences) { 146 | return new RSVP.Promise((resolve) => {resolve(globalReferences);}); 147 | } else { 148 | const url = urls.globalReferencesUrl(externalId); 149 | return Ember.$.getJSON(url).then((refs) => { 150 | this.globalReferences[externalId] = refs; 151 | return refs; 152 | }); 153 | } 154 | }, 155 | loadHoogleDocs(packageId,moduleName,entity,name) { 156 | const id = packageId + "/" + moduleName + "/" + entity + "/" + name; 157 | const hoogleDoc = this.hoogleDocs[id]; 158 | if(hoogleDoc) { 159 | return new RSVP.Promise((resolve)=>{resolve(hoogleDoc);}); 160 | } else { 161 | const url = urls.hoogleDocsUrl(packageId,moduleName,entity,name); 162 | return Ember.$.getJSON(url).then((hoogleDoc) => { 163 | this.hoogleDocs[id] = hoogleDoc; 164 | return hoogleDoc; 165 | }); 166 | } 167 | } 168 | }); 169 | -------------------------------------------------------------------------------- /javascript/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | {{#link-to 'packages'}}Haskell packages{{/link-to}} 7 | About 8 | 9 | {{#each themes as |theme|}} 10 | {{#radio-button 11 | value=theme.id 12 | groupValue=currentTheme 13 | changed="themeChanged"}} 14 | {{theme.name}} 15 | {{/radio-button}} 16 | {{/each}} 17 | 18 |
19 |
20 |
21 |
22 | {{outlet}} 23 |
24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /javascript/app/templates/bad-url.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Not found

4 |
Main page
5 |
6 |
7 | -------------------------------------------------------------------------------- /javascript/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /javascript/app/templates/components/bottom-panel.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{yield "header"}} 3 | 4 | Close 5 | 6 |
7 |
8 | {{yield "body"}} 9 |
10 | -------------------------------------------------------------------------------- /javascript/app/templates/components/expression-info.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#each expressions as |expression|}} 3 |
4 | {{expression.sourceCode}} 5 |
:: {{type-signature type=expression.info.exprType identifiers=identifiers currentLineNumber=currentLineNumber}} 6 |
7 | {{/each}} 8 |
9 | -------------------------------------------------------------------------------- /javascript/app/templates/components/file-tree.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{input class="form-control" value=query placeholder="Filename"}}Hide 4 |
5 |
6 | {{#radio-button 7 | value="alphabetical" 8 | groupValue=sortType}} 9 | Sort by name 10 | {{/radio-button}} 11 | {{#radio-button 12 | value="type" 13 | groupValue=sortType}} 14 | Sort by type 15 | {{/radio-button}} 16 |
17 |
18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /javascript/app/templates/components/haskell-module.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#if declarations}} 3 |
4 |
5 |
6 | {{input class="form-control" value=query placeholder="Identifier"}}{{showDeclarationsLabel}} 7 |
8 |
9 | {{#if showDeclarations}} 10 |
11 | 21 |
22 | {{/if}} 23 |
24 | {{/if}} 25 | {{#info-window 26 | targetElement=selectedIdentifier 27 | isHoveredOverIdentifier=isHoveredOverIdentifier 28 | hasSelectedExpression=hasSelectedExpression 29 | containerElementId="right-panel" as |section|}} 30 | {{#if (eq section "header")}} 31 | {{#if hasSelectedExpression}} 32 |
Selected expressions
33 | {{else}} 34 | {{identifier-name 35 | identifierInfo=identifierInfo 36 | identifierOccurrence=identifierOccurrence 37 | identifierElement=selectedIdentifier 38 | currentPackageId=packageId 39 | isBinder=identifierOccurrence.isBinder 40 | currentLineNumber=currentLineNumber 41 | findReferences=findReferences}} 42 | {{/if}} 43 | {{else}} 44 | {{#unless hasSelectedExpression}} 45 | {{identifier-info 46 | identifierInfo=identifierInfo 47 | identifierOccurrence=identifierOccurrence 48 | identifiers=identifiers 49 | currentLineNumber=currentLineNumber}} 50 | {{else}} 51 | {{expression-info 52 | expressions=expressions 53 | identifiers=identifiers 54 | currentLineNumber=currentLineNumber}} 55 | {{/unless}} 56 | {{/if}} 57 | {{/info-window}} 58 | -------------------------------------------------------------------------------- /javascript/app/templates/components/identifier-info.hbs: -------------------------------------------------------------------------------- 1 | {{#if (or identifierInfo identifierOccurrence.idOccType)}} 2 |
3 | {{#if identifierInfo}} 4 | {{#if isNaughtyRecSel}} 5 | This record selector can never be called because its type mentions a type variable that isn't in the result type of the constructor 6 | {{else}} 7 | {{type-signature 8 | type=identifierInfo.idType 9 | identifiers=identifiers 10 | currentLineNumber=currentLineNumber}} 11 | {{/if}} 12 | {{/if}} 13 | {{#if (and identifierInfo identifierOccurrence identifierOccurrence.idOccType)}} 14 |
15 | {{/if}} 16 | {{#if (and identifierOccurrence identifierOccurrence.idOccType)}} 17 | {{type-signature 18 | type=identifierOccurrence.idOccType 19 | identifiers=identifiers 20 | currentLineNumber=currentLineNumber}} 21 | {{/if}} 22 |
23 | {{{identifierInfo.doc}}} 24 | {{{downloadedDocumentation}}} 25 |
26 | {{#if identifierOccurrence.instanceResolution}} 27 |
28 | {{/if}} 29 |
30 | {{#if identifierOccurrence.instanceResolution}} 31 | {{instance-info 32 | instance=identifierOccurrence.instanceResolution 33 | identifiers=identifiers 34 | nestedLevel=0 35 | currentLineNumber=currentLineNumber}} 36 | {{/if}} 37 |
38 |
39 | {{/if}} 40 | -------------------------------------------------------------------------------- /javascript/app/templates/components/identifier-name.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#if identifierInfo.demangledOccName}}{{identifierInfo.demangledOccName}}{{else}}{{name}}{{/if}} 3 | {{#unless isBinder}} 4 | {{#if location}} 5 | {{location}} 6 | Go to definition 7 | {{/if}} 8 | {{/unless}} 9 | {{#if isExternalIdentifier}} 10 | Find references 11 | {{/if}} 12 |
13 | -------------------------------------------------------------------------------- /javascript/app/templates/components/infinite-list.hbs: -------------------------------------------------------------------------------- 1 | {{#each renderedElements as |element|}} 2 | {{yield element}} 3 | {{/each}} 4 | -------------------------------------------------------------------------------- /javascript/app/templates/components/info-window.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 | {{yield "header"}} 8 |
9 |
10 | {{yield "body"}} 11 |
12 |
13 | -------------------------------------------------------------------------------- /javascript/app/templates/components/input-with-autocomplete.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{input class="form-control search-input" value=query placeholder=placeholder}} 3 |
4 | {{# if items}} 5 | 14 | {{/if}} 15 |
16 |
17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /javascript/app/templates/components/instance-info.hbs: -------------------------------------------------------------------------------- 1 | {{#if instance}} 2 |
3 | 4 | instance {{type-signature type=instance.instanceType identifiers=identifiers currentLineNumber=currentLineNumber noExpand=true}} 5 | (Go to definition) 6 | {{#each instance.instances as |inst|}} 7 | {{instance-info instance=inst identifiers=identifiers nestedLevel=nextNestedLevel currentLineNumber=currentLineNumber}} 8 | {{/each}} 9 |
10 | {{/if}} 11 | -------------------------------------------------------------------------------- /javascript/app/templates/components/paginated-list.hbs: -------------------------------------------------------------------------------- 1 |
2 | Found {{total}} {{{foundWhere}}} 3 | {{#if (or next prev)}} 4 |   5 |   6 | 7 | {{#if first}}{{/if}} 8 | {{#if prev}}{{/if}} 9 | {{firstItemOnPage}} - {{lastItemOnPage}} 10 | {{#if next}}{{/if}} 11 | {{#if last}}{{/if}} 12 | 13 | {{/if}} 14 |
15 |
16 | {{yield items}} 17 |
18 | -------------------------------------------------------------------------------- /javascript/app/templates/components/resizable-panel.hbs: -------------------------------------------------------------------------------- 1 |
{{yield (action "hide")}} 2 |
3 | {{{hideButtonLabel}}} 4 |
5 |
6 | -------------------------------------------------------------------------------- /javascript/app/templates/components/text-file.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{{html}}} 3 |
4 | -------------------------------------------------------------------------------- /javascript/app/templates/components/type-component.hbs: -------------------------------------------------------------------------------- 1 | {{#if (and expanded identifierInfo)}}({{/if}} 2 | {{occName}} 3 | {{#if (and expanded identifierInfo)}} :: {{type-signature type=identifierInfo.idType identifiers=identifiers noExpand=true}}){{/if}} 4 | -------------------------------------------------------------------------------- /javascript/app/templates/components/type-signature-text.hbs: -------------------------------------------------------------------------------- 1 | {{!-- No newlines to get rid of spaces between spans --}} 2 | {{#each components as |typeComponent|}}{{#if (eq typeComponent.tag "Text")}}{{typeComponent.contents}}{{else}}{{typeComponent.name}}{{/if}}{{/each}} 3 | -------------------------------------------------------------------------------- /javascript/app/templates/components/type-signature.hbs: -------------------------------------------------------------------------------- 1 | {{!-- No newlines to get rid of spaces between spans --}} 2 | {{#each components as |typeComponent|}}{{#if (eq typeComponent.tag "Text")}}{{typeComponent.contents}}{{else}}{{type-component occName=typeComponent.name internalId=typeComponent.internalId identifiers=identifiers currentLineNumber=currentLineNumber}}{{/if}}{{/each}} 3 | {{#unless noExpand}} 4 | {{#if type.componentsExpanded}} 5 |
6 | 8 |
9 | {{/if}} 10 | {{/unless}} 11 | -------------------------------------------------------------------------------- /javascript/app/templates/package.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{#link-to 'package' model}}{{model.id}}{{/link-to}} 4 | 5 | {{#input-with-autocomplete 6 | onSubmit=(action 'searchIdentifier') 7 | createSearchUrlFunction=createSearchUrlFunction 8 | maxItems=10 9 | selectItem=(action 'showIdentifier') 10 | searchButtonText="Search" 11 | placeholder="Identifier" as |identifier|}} 12 | {{identifier.demangledOccName}} :: {{type-signature-text components=identifier.idType.components}} 13 |
14 | {{identifier.locationInfo.packageId.name}}-{{identifier.locationInfo.packageId.version}} 15 | {{#if identifier.locationInfo.modulePath}} 16 | {{identifier.locationInfo.modulePath}} 17 | {{else}} 18 | {{identifier.locationInfo.moduleName}} 19 | {{/if}} 20 |
21 | {{/input-with-autocomplete}} 22 |
23 | {{#radio-button 24 | value="currentPackage" 25 | groupValue=searchMode}} 26 | in {{model.id}} 27 | {{/radio-button}} 28 | {{#radio-button 29 | value="allPackages" 30 | groupValue=searchMode}} 31 | in all packages 32 | {{/radio-button}} 33 | {{#if currentFile}} 34 | 35 | {{currentFile}} 36 | 37 | {{/if}} 38 |
39 |
40 | {{#resizable-panel class="left-panel" alsoResizeElementId="#right-panel" as |hide|}} 41 |
42 | {{file-tree directoryTree=model.directoryTree openFile="openFile" currentFile=currentFile packageId=model.id hide=hide}} 43 |
44 | {{/resizable-panel}} 45 |
46 |
47 | {{outlet}} 48 |
49 | {{#bottom-panel visible=bottomPanelVisible topPanelElementId="#file-container" containerElementId="#right-panel" as |section|}} 50 | {{#if (eq section "header")}} 51 | References to {{occName}} 52 | {{#if locationInfo.packageId}} 53 | (defined in {{locationInfo.packageId.name}}-{{locationInfo.packageId.version}} / {{locationInfo.moduleName}}) 54 | {{/if}} 55 | {{else}} 56 |
57 | {{#if globalReferences}} 58 | 59 | {{#each globalReferences as |ref|}} 60 |
61 | 62 | {{ref.packageId}} 63 | ({{ref.count}}) 64 |
65 | {{/each}} 66 |
67 | {{/if}} 68 |
69 |
70 | {{#paginated-list url=referencesUrl foundWhere=(concat "in " packageId "") as |files|}} 71 | 81 | {{/paginated-list}} 82 |
83 | {{/if}} 84 | {{/bottom-panel}} 85 |
86 |
87 |
88 | -------------------------------------------------------------------------------- /javascript/app/templates/package/index.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{input class="form-control" type="text" value=query placeholder="Module name"}} 4 |
5 | 12 |
13 | -------------------------------------------------------------------------------- /javascript/app/templates/package/search.hbs: -------------------------------------------------------------------------------- 1 |
2 | Query : {{model.query}} 3 |
4 |
5 | {{#paginated-list url=model.url as |identifiers|}} 6 | 20 | {{/paginated-list}} 21 |
22 | -------------------------------------------------------------------------------- /javascript/app/templates/package/show.hbs: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /javascript/app/templates/package/show/file.hbs: -------------------------------------------------------------------------------- 1 | {{#if model.isHaskellModule}} 2 | {{haskell-module 3 | path=model.id 4 | name=model.name 5 | packageId=model.packageId 6 | componentId=model.componentId 7 | html=model.sourceCodeHtml 8 | identifiers=model.identifiers 9 | occurrences=model.occurrences 10 | colorTheme=settings.colorTheme 11 | declarations=model.declarations 12 | findReferences=(action "findReferences") 13 | }} 14 | {{else}} 15 | {{text-file text=model.text path=model.id}} 16 | {{/if}} 17 | -------------------------------------------------------------------------------- /javascript/app/templates/packages.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {{#input-with-autocomplete 5 | onSubmit=(action 'searchIdentifier') 6 | createSearchUrlFunction=createSearchUrlFunction 7 | maxItems=10 8 | selectItem=(action 'showIdentifier') 9 | searchButtonText="Search in all packages" 10 | placeholder="Haskell identifier" as |identifier|}} 11 | {{identifier.demangledOccName}} :: {{type-signature-text components=identifier.idType.components}} 12 |
13 | {{identifier.locationInfo.packageId.name}}-{{identifier.locationInfo.packageId.version}} 14 | {{#if identifier.locationInfo.modulePath}} 15 | {{identifier.locationInfo.modulePath}} 16 | {{else}} 17 | {{identifier.locationInfo.moduleName}} 18 | {{/if}} 19 |
20 | {{/input-with-autocomplete}} 21 |
22 |
23 |
24 |
25 | {{input class="form-control" type="text" value=query placeholder="Package name"}} 26 | Number of packages : {{packages.length}} 27 |
28 |
29 |
30 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /javascript/app/templates/search.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | {{#input-with-autocomplete 6 | onSubmit=(action 'searchIdentifier') 7 | createSearchUrlFunction=createSearchUrlFunction 8 | maxItems=10 9 | selectItem=(action 'showIdentifier') 10 | searchButtonText="Search in all packages" 11 | placeholder="Haskell identifier" as |identifier|}} 12 | {{identifier.demangledOccName}} :: {{type-signature-text components=identifier.idType.components}} 13 |
14 | {{identifier.locationInfo.packageId.name}}-{{identifier.locationInfo.packageId.version}} 15 | {{#if identifier.locationInfo.modulePath}} 16 | {{identifier.locationInfo.modulePath}} 17 | {{else}} 18 | {{identifier.locationInfo.moduleName}} 19 | {{/if}} 20 |
21 | {{/input-with-autocomplete}} 22 |
23 |
24 | Query : {{model.query}} 25 |
26 |
27 | {{#paginated-list url=model.url as |identifiers|}} 28 |
    29 | {{#each identifiers as |identifier|}} 30 |
  • 31 | {{identifier.demangledOccName}} :: {{type-signature-text components=identifier.idType.components}} 32 | 33 |
    34 | {{#if identifier.locationInfo.modulePath}} 35 | Defined in {{identifier.locationInfo.packageId.name}}-{{identifier.locationInfo.packageId.version}} {{identifier.locationInfo.modulePath}} 36 | {{else}} 37 | Defined in {{identifier.locationInfo.packageId.name}}-{{identifier.locationInfo.packageId.version}} {{identifier.locationInfo.moduleName}} 38 | {{/if}} 39 |
    40 |
    {{{identifier.doc}}}
    41 |
  • 42 | {{/each}} 43 |
44 | {{/paginated-list}} 45 |
46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /javascript/app/utils/api-urls.js: -------------------------------------------------------------------------------- 1 | import config from '../config/environment'; 2 | 3 | // "." and ".." is a special case because of the Path Segment Normalization: 4 | // https://tools.ietf.org/html/rfc3986#section-6.2.2.3 5 | // The segments “..” and “.” can be removed from a URL by a browser. 6 | // https://stackoverflow.com/questions/3856693/a-url-resource-that-is-a-dot-2e 7 | function fixDots(string) { 8 | if(string === ".") { 9 | return "%20%2E"; 10 | } 11 | else if(string === "..") { 12 | return "%20%2E%2E"; 13 | } else { 14 | return string.replace(/\./g, '%2E'); 15 | } 16 | } 17 | 18 | export const urls = { 19 | packageInfoUrl : function(packageId) { 20 | return config.APP.staticUrlPrefix+"/"+packageId+"/"+config.APP.haskellCodeExplorerDirectory+"/packageInfo.json"; 21 | }, 22 | fileUrl : function(packageId,filePath) { 23 | return config.APP.staticUrlPrefix+"/"+packageId+"/"+filePath; 24 | }, 25 | haskellModuleUrl : function (packageId,filePath) { 26 | return config.APP.staticUrlPrefix+"/"+packageId+"/"+config.APP.haskellCodeExplorerDirectory+"/"+encodeURIComponent(encodeURIComponent(filePath))+ ".json"; 27 | }, 28 | packagesUrl : config.APP.apiUrlPrefix + "/packages", 29 | identifierDefinitionSiteUrl : function(packageId,moduleName,componentId,entity,name) { 30 | return config.APP.apiUrlPrefix + "/definitionSite/" + packageId+"/"+componentId+"/"+moduleName+"/"+entity+"/"+fixDots(encodeURIComponent(name)); 31 | }, 32 | modulePathUrl : function (packageId,moduleName,componentId) { 33 | return config.APP.apiUrlPrefix + "/modulePath/"+packageId+"/"+componentId+"/"+moduleName; 34 | }, 35 | expressionsUrl : function (packageId,modulePath,lineStart,columnStart,lineEnd,columnEnd) { 36 | return config.APP.apiUrlPrefix + "/expressions/"+packageId+"/"+encodeURIComponent(modulePath) +"/"+lineStart+"/"+columnStart+"/"+lineEnd+"/"+columnEnd; 37 | }, 38 | referencesUrl : function (packageId,externalId) { 39 | return config.APP.apiUrlPrefix + "/references/"+packageId+"/"+encodeURIComponent(externalId); 40 | }, 41 | identifierSearchUrl : function (packageId,query) { 42 | return config.APP.apiUrlPrefix + "/identifiers/"+packageId+"/"+fixDots(encodeURIComponent(query)); 43 | }, 44 | globalReferencesUrl : function (externalId) { 45 | return config.APP.apiUrlPrefix + "/globalReferences/"+encodeURIComponent(externalId); 46 | }, 47 | globalIdentifiersUrl : function (query) { 48 | return config.APP.apiUrlPrefix + "/globalIdentifiers/"+fixDots(encodeURIComponent(query)); 49 | }, 50 | hoogleDocsUrl : function (packageId,moduleName,entity,name) { 51 | return config.APP.apiUrlPrefix + "/hoogleDocs/"+packageId+"/"+encodeURIComponent(moduleName)+"/"+entity+"/"+fixDots(encodeURIComponent(name)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /javascript/app/utils/go-to-definition.js: -------------------------------------------------------------------------------- 1 | function exactLocationToUrl(exactLocation) { 2 | const modulePath = exactLocation.modulePath; 3 | const packageId = exactLocation.packageId.name + "-" + exactLocation.packageId.version; 4 | let hash = ""; 5 | if(exactLocation.startLine != 1) { 6 | hash = "#L" + exactLocation.startLine; 7 | } 8 | return "/package/"+packageId+"/show/"+modulePath+hash; 9 | } 10 | 11 | function hackageUrl(packageId,locationInfo) { 12 | let hash = ""; 13 | if (locationInfo.entity !== "Mod") { 14 | hash = "#" + encodeURIComponent(locationInfo.name); 15 | } 16 | return "https://hackage.haskell.org/package/"+packageId+"/docs/src/"+locationInfo.moduleName+".html"+hash; 17 | } 18 | 19 | function openUrl(buttonId,url) { 20 | if(buttonId === 2) {//middle mouse button 21 | window.open(url, '_blank'); 22 | } else if(buttonId == 1) {//left mouse button 23 | window.location = url; 24 | } 25 | return false; 26 | } 27 | 28 | function saveCurrentLocation(currentLineNumber) { 29 | if(currentLineNumber) { 30 | const url = window.location.origin + window.location.pathname + "#L" + currentLineNumber; 31 | if(location.href != url) { 32 | window.location.hash = "#L" + currentLineNumber; 33 | } 34 | } 35 | } 36 | 37 | function goToDefinition(store,locationInfo,buttonId,currentLineNumber) { 38 | if(locationInfo.tag === "ExactLocation") { 39 | const url = exactLocationToUrl(locationInfo); 40 | if(locationInfo.startLine !== currentLineNumber) { 41 | saveCurrentLocation(currentLineNumber); 42 | } 43 | openUrl(buttonId,url); 44 | } else if((locationInfo.tag === "ApproximateLocation") && 45 | (locationInfo.moduleName.indexOf("Paths_") !== 0)) { 46 | const packageId = locationInfo.packageId.name+"-"+locationInfo.packageId.version; 47 | if(locationInfo.entity === "Mod") { 48 | store.loadDefinitionSite(packageId, 49 | locationInfo.moduleName, 50 | locationInfo.componentId, 51 | locationInfo.entity, 52 | locationInfo.moduleName) 53 | .then((defSite) => { 54 | const packageId = defSite.location.packageId.name + "-" + defSite.location.packageId.version; 55 | openUrl(buttonId,"/package/" + packageId + "/show/" + defSite.location.modulePath); 56 | }).catch(() => { 57 | openUrl(buttonId,hackageUrl(packageId,locationInfo)); 58 | }); 59 | } else { 60 | store.loadDefinitionSite(packageId, 61 | locationInfo.moduleName, 62 | locationInfo.componentId, 63 | locationInfo.entity, 64 | locationInfo.name) 65 | .then((definitionSite) => { 66 | if(definitionSite.location.tag === "ExactLocation") { 67 | const url = exactLocationToUrl(definitionSite.location); 68 | if(locationInfo.startLine !== currentLineNumber) { 69 | saveCurrentLocation(currentLineNumber); 70 | } 71 | openUrl(buttonId,url); 72 | } else { 73 | saveCurrentLocation(currentLineNumber); 74 | openUrl(buttonId,hackageUrl(packageId,locationInfo)); 75 | } 76 | }).catch((e) => { 77 | console.log(e); 78 | saveCurrentLocation(currentLineNumber); 79 | openUrl(buttonId,hackageUrl(packageId,locationInfo)); 80 | }); 81 | } 82 | } else { 83 | alert('No location info'); 84 | } 85 | } 86 | 87 | export { 88 | goToDefinition,openUrl 89 | } 90 | -------------------------------------------------------------------------------- /javascript/app/utils/line-selection.js: -------------------------------------------------------------------------------- 1 | function initializeLineSelection(sourceCodeContainerElement,component) { 2 | const lineNumbers = Array.prototype.slice.call(sourceCodeContainerElement.querySelectorAll("td.line-number")); 3 | if(lineNumbers.length > 0) { 4 | const onhashchange = function () { 5 | highlightSelectedLines(sourceCodeContainerElement); 6 | } 7 | window.addEventListener("hashchange",onhashchange); 8 | component._onhashchange = onhashchange; 9 | 10 | let shiftPressed; 11 | const onkeydown = function (event) { 12 | if(event.keyCode === 16) { shiftPressed = true; } 13 | }; 14 | const onkeyup = function (event) { 15 | if(event.keyCode === 16) { shiftPressed = false; } 16 | }; 17 | 18 | document.addEventListener('keydown',onkeydown); 19 | document.addEventListener('keyup',onkeyup); 20 | component._onkeydown = onkeydown; 21 | component._onkeyup = onkeyup; 22 | 23 | let selectedLine1,selectedLine2; 24 | lineNumbers.forEach((lineNumberElement) => { 25 | lineNumberElement.onclick = function() { 26 | const number = parseInt(this.textContent); 27 | if(shiftPressed && selectedLine1) { 28 | if(selectedLine1 != number) { 29 | selectedLine2 = number; 30 | if(selectedLine1 < selectedLine2) { 31 | highlightLines(sourceCodeContainerElement,selectedLine1,selectedLine2); 32 | window.location.hash = "L"+selectedLine1+"-L"+selectedLine2; 33 | } else { 34 | highlightLines(sourceCodeContainerElement,selectedLine2,selectedLine1); 35 | window.location.hash = "L"+selectedLine2+"-L"+selectedLine1; 36 | } 37 | } 38 | } else { 39 | selectedLine1 = number; 40 | selectedLine2 = null; 41 | highlightLines(sourceCodeContainerElement,selectedLine1,selectedLine1); 42 | window.location.hash = "L"+number; 43 | } 44 | } 45 | }); 46 | const lines = highlightSelectedLines(sourceCodeContainerElement); 47 | if(lines.length) { 48 | selectedLine1 = lines[0]; 49 | selectedLine2 = lines[1]; 50 | } 51 | } 52 | } 53 | 54 | function highlightSelectedLines (sourceCodeContainerElement) { 55 | const lineInfo = window.location.hash.slice(1); 56 | if(lineInfo) { 57 | if(lineInfo.includes('-')) { 58 | const lines = lineInfo.split("-"); 59 | const lineNumber1 = parseInt(lines[0].substring(1)); 60 | const lineNumber2 = parseInt(lines[1].substring(1)); 61 | if(lineNumber1 && lineNumber2 && lineNumber1 <= lineNumber2) { 62 | highlightLines(sourceCodeContainerElement,lineNumber1,lineNumber2); 63 | const line = sourceCodeContainerElement.querySelector("td#LC"+lineNumber1); 64 | if(line) { 65 | scrollLineIntoView(line,sourceCodeContainerElement); 66 | } 67 | return [lineNumber1,lineNumber2]; 68 | } 69 | } else { 70 | const lineNumber = parseInt(lineInfo.substring(1)); 71 | if(lineNumber) { 72 | highlightLines(sourceCodeContainerElement,lineNumber,lineNumber); 73 | const line = sourceCodeContainerElement.querySelector("td#LC"+lineNumber); 74 | if(line) { 75 | scrollLineIntoView(line,sourceCodeContainerElement); 76 | } 77 | return [lineNumber]; 78 | } 79 | } 80 | } else { 81 | highlightLines(sourceCodeContainerElement,0,0); 82 | return []; 83 | } 84 | } 85 | 86 | function scrollLineIntoView(lineElement,sourceCodeContainerElement) { 87 | lineElement.parentNode.scrollIntoView(); 88 | const container = sourceCodeContainerElement.parentNode.parentNode; 89 | const windowHeight = container.offsetHeight; 90 | const fullHeight = sourceCodeContainerElement.offsetHeight; 91 | 92 | if(fullHeight - container.scrollTop > windowHeight) { 93 | container.scrollTop = container.scrollTop - (windowHeight/2 - 20); 94 | } 95 | } 96 | 97 | function highlightLines(parentElement,startLine,endLine) { 98 | const lineElements = Array.prototype.slice.call(parentElement.querySelectorAll("td.line-content")); 99 | lineElements.forEach((lineElement) => { 100 | const number = parseInt(lineElement.id.substring(2)); //... 101 | if(number >= startLine && number <= endLine) { 102 | lineElement.classList.add('highlighted-line'); 103 | } else { 104 | lineElement.classList.remove('highlighted-line'); 105 | } 106 | }); 107 | } 108 | 109 | export { 110 | initializeLineSelection,highlightLines,highlightSelectedLines 111 | } 112 | -------------------------------------------------------------------------------- /javascript/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "haskell-code-explorer", 3 | "dependencies": { 4 | "pace": "^1.0.2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /javascript/config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = function(environment) { 5 | let ENV = { 6 | modulePrefix: 'haskell-code-explorer', 7 | environment, 8 | rootURL: '/', 9 | locationType: 'auto', 10 | EmberENV: { 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. 'with-controller': true 14 | }, 15 | EXTEND_PROTOTYPES: { 16 | // Prevent Ember Data from overriding Date.parse. 17 | Date: false 18 | } 19 | }, 20 | pace: { 21 | theme: 'minimal', 22 | color: 'silver', 23 | target: 'body', 24 | elements: { 25 | checkInterval: 100, 26 | selectors: ['body', '.ember-view'] 27 | }, 28 | ajax: false 29 | }, 30 | APP: { 31 | staticUrlPrefix: "/files", 32 | apiUrlPrefix: "/api", 33 | haskellCodeExplorerDirectory: ".haskell-code-explorer", 34 | title: "Haskell Code Explorer" 35 | } 36 | }; 37 | 38 | if (environment === 'development') { 39 | // ENV.APP.LOG_RESOLVER = true; 40 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 41 | // ENV.APP.LOG_TRANSITIONS = true; 42 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 43 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 44 | } 45 | 46 | if (environment === 'test') { 47 | // Testem prefers this... 48 | ENV.locationType = 'none'; 49 | 50 | // keep test console output quieter 51 | ENV.APP.LOG_ACTIVE_GENERATION = false; 52 | ENV.APP.LOG_VIEW_LOOKUPS = false; 53 | 54 | ENV.APP.autoboot = false; 55 | 56 | ENV.APP.rootElement = '#ember-testing'; 57 | } 58 | 59 | if (environment === 'production') { 60 | 61 | } 62 | 63 | return ENV; 64 | }; 65 | -------------------------------------------------------------------------------- /javascript/config/targets.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | browsers: [ 4 | 'ie 9', 5 | 'last 1 Chrome versions', 6 | 'last 1 Firefox versions', 7 | 'last 1 Safari versions' 8 | ] 9 | }; 10 | -------------------------------------------------------------------------------- /javascript/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 5 | 6 | module.exports = function(defaults) { 7 | let app = new EmberApp(defaults, { 8 | 'ember-cli-bootstrap-4': { 9 | js: null 10 | }, 11 | 'ember-cli-babel': { 12 | includePolyfill: true 13 | } 14 | }); 15 | app.import('vendor/jquery-ui-1.12.1.custom/jquery-ui.min.js')// only draggable and resizable 16 | app.import('vendor/jquery-ui-1.12.1.custom/jquery-ui.structure.min.css') 17 | app.import('node_modules/jstree/dist/jstree.min.js'); 18 | app.import('node_modules/jstree/dist/themes/default/style.min.css'); 19 | app.import('node_modules/showdown/dist/showdown.min.js'); 20 | return app.toTree(); 21 | }; 22 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "haskell-code-explorer", 3 | "version": "0.0.1", 4 | "description": "Haskell Code Explorer", 5 | "private": false, 6 | "license": "MIT", 7 | "author": "alexwl", 8 | "directories": { 9 | "doc": "doc", 10 | "test": "tests" 11 | }, 12 | "repository": "", 13 | "scripts": { 14 | "build": "ember build --environment=production", 15 | "release": "ember build --environment=production --output-path=release", 16 | "start": "ember server" 17 | }, 18 | "devDependencies": { 19 | "broccoli-asset-rev": "^2.4.5", 20 | "ember-cli": "^3.2.0", 21 | "ember-cli-app-version": "^3.0.0", 22 | "ember-cli-babel": "^6.12.0", 23 | "ember-cli-bootstrap-4": "^0.5.6", 24 | "ember-cli-dependency-checker": "^1.3.0", 25 | "ember-cli-eslint": "^4.2.1", 26 | "ember-cli-htmlbars": "^2.0.1", 27 | "ember-cli-htmlbars-inline-precompile": "^1.0.0", 28 | "ember-cli-inject-live-reload": "^1.4.1", 29 | "ember-cli-pace": "^0.1.0", 30 | "ember-cli-qunit": "^4.1.1", 31 | "ember-cli-sass": "^7.1.7", 32 | "ember-cli-shims": "^1.1.0", 33 | "ember-cli-sri": "^2.1.0", 34 | "ember-cli-uglify": "^1.2.0", 35 | "ember-export-application-global": "^2.0.0", 36 | "ember-load-initializers": "^1.0.0", 37 | "ember-radio-button": "^1.1.1", 38 | "ember-resolver": "^4.0.0", 39 | "ember-sinon": "^1.0.1", 40 | "ember-sinon-qunit": "^2.0.0", 41 | "ember-source": "3.2.0", 42 | "ember-truth-helpers": "^2.0.0", 43 | "express": "^4.16.2", 44 | "glob": "^4.5.3", 45 | "jstree": "3.3.9", 46 | "loader.js": "^4.2.3", 47 | "morgan": "^1.9.0", 48 | "showdown": "^1.8.6" 49 | }, 50 | "engines": { 51 | "node": "^4.5 || 6.* || >= 7.*" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /javascript/public/assets/32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/public/assets/32px.png -------------------------------------------------------------------------------- /javascript/public/assets/40px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/public/assets/40px.png -------------------------------------------------------------------------------- /javascript/public/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/public/assets/favicon.ico -------------------------------------------------------------------------------- /javascript/public/assets/haskell.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/public/assets/haskell.ico -------------------------------------------------------------------------------- /javascript/public/assets/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/public/assets/throbber.gif -------------------------------------------------------------------------------- /javascript/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /javascript/release/assets/32px-eebaf260766f5e0e773f53d3ea4f3e4d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/release/assets/32px-eebaf260766f5e0e773f53d3ea4f3e4d.png -------------------------------------------------------------------------------- /javascript/release/assets/40px-51286e68b083696edaf4f9fc577e2a2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/release/assets/40px-51286e68b083696edaf4f9fc577e2a2d.png -------------------------------------------------------------------------------- /javascript/release/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/release/assets/favicon.ico -------------------------------------------------------------------------------- /javascript/release/assets/haskell.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/release/assets/haskell.ico -------------------------------------------------------------------------------- /javascript/release/assets/throbber-62be6ed2b189444b472b8000dc187240.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/release/assets/throbber-62be6ed2b189444b472b8000dc187240.gif -------------------------------------------------------------------------------- /javascript/release/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /javascript/server/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /javascript/server/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | //https://discuss.emberjs.com/t/how-to-disable-http-mock-server-within-environment-config-file/6660/9 5 | 6 | //To use http mocks from server/mocks for testing: 7 | //ember server 8 | //To use real server: 9 | //ember server --proxy=http://localhost:8080/ 10 | 11 | function usingProxy() { 12 | return !!process.argv.filter(function (arg) { 13 | return arg.indexOf('--proxy') === 0; 14 | }).length; 15 | } 16 | 17 | module.exports = function(app) { 18 | if (usingProxy()) { return; } 19 | 20 | const globSync = require('glob').sync; 21 | const mocks = globSync('./mocks/**/*.js', { cwd: __dirname }).map(require); 22 | const proxies = globSync('./proxies/**/*.js', { cwd: __dirname }).map(require); 23 | 24 | // Log proxy requests 25 | const morgan = require('morgan'); 26 | app.use(morgan('dev')); 27 | 28 | mocks.forEach(route => route(app)); 29 | proxies.forEach(route => route(app)); 30 | }; 31 | -------------------------------------------------------------------------------- /javascript/testem.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | test_page: 'tests/index.html?hidepassed', 4 | disable_watching: true, 5 | launch_in_ci: [ 6 | 'Chrome' 7 | ], 8 | launch_in_dev: [ 9 | 'Chrome' 10 | ], 11 | browser_args: { 12 | Chrome: { 13 | mode: 'ci', 14 | args: [ 15 | // --no-sandbox is needed when running Chrome inside a container 16 | process.env.TRAVIS ? '--no-sandbox' : null, 17 | '--disable-gpu', 18 | '--headless', 19 | '--remote-debugging-port=0', 20 | '--window-size=1440,900' 21 | ].filter(Boolean) 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /javascript/tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | embertest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /javascript/tests/acceptance/haskell-module-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { visit, currentURL, find } from '@ember/test-helpers'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | 6 | module('Acceptance | haskell-module', function(hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | //Test data is in 'server/mocks/packages.js' 10 | test('visiting /package/test-package-0.1.0.0/show/app/Main.hs', async function(assert) { 11 | await visit('/package/test-package-0.1.0.0/show/app/Main.hs'); 12 | 13 | assert.equal(currentURL(), '/package/test-package-0.1.0.0/show/app/Main.hs'); 14 | 15 | const lines = Array.from(find('tbody').children).map((tr) => { 16 | return tr.children[1].innerText; 17 | }); 18 | 19 | assert.deepEqual(lines,["module Main where", 20 | "", 21 | "import Lib", 22 | "", 23 | "main :: IO ()", 24 | "main = someFunc", 25 | "", 26 | ""]); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /javascript/tests/acceptance/package-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { visit, currentURL, find } from '@ember/test-helpers'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | 6 | module('Acceptance | package', function(hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | //Test data is in 'server/mocks/packages.js' 10 | test('visiting /package/test-package-0.1.0.0', async function(assert) { 11 | await visit('/package/test-package-0.1.0.0'); 12 | assert.equal(currentURL(), '/package/test-package-0.1.0.0'); 13 | 14 | const moduleNames = Array.from(find('ul.modules').children).map((li) => { 15 | return [li.innerText,li.children[0].getAttribute('href')]; 16 | }); 17 | 18 | assert.deepEqual(moduleNames,[["app/Main.hs","/package/test-package-0.1.0.0/show/app/Main.hs"], 19 | ["src/Lib.hs","/package/test-package-0.1.0.0/show/src/Lib.hs"], 20 | ["src/Types.hs","/package/test-package-0.1.0.0/show/src/Types.hs"], 21 | ["test/Spec.hs","/package/test-package-0.1.0.0/show/test/Spec.hs"]]); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /javascript/tests/acceptance/packages-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { visit, currentURL, find } from '@ember/test-helpers'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | module('Acceptance | packages', function(hooks) { 6 | setupApplicationTest(hooks); 7 | 8 | //Test data is in file 'server/mocks/packages.js' 9 | test('visiting /', async function(assert) { 10 | await visit('/'); 11 | assert.equal(currentURL(), '/'); 12 | const linkToPackage = find('ul a:first-child'); 13 | assert.equal(linkToPackage.innerText, 'test-package-0.1.0.0'); 14 | assert.equal(linkToPackage.getAttribute('href'), '/package/test-package-0.1.0.0'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /javascript/tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /javascript/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HaskellCodeExplorer Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /javascript/tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/tests/integration/.gitkeep -------------------------------------------------------------------------------- /javascript/tests/integration/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/tests/integration/components/.gitkeep -------------------------------------------------------------------------------- /javascript/tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from '../app'; 2 | import config from '../config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | 8 | start(); 9 | -------------------------------------------------------------------------------- /javascript/vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/.gitkeep -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery-ui 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | Copyright and related rights for sample code are waived via CC0. Sample 34 | code is defined as all source code contained within the demos directory. 35 | 36 | CC0: http://creativecommons.org/publicdomain/zero/1.0/ 37 | 38 | ==== 39 | 40 | All files located in the node_modules and external directories are 41 | externally maintained libraries used by this software which have their 42 | own licenses; we recommend you read them, as their terms may differ from 43 | the terms above. 44 | -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_777620_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_777620_256x240.png -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_777777_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_777777_256x240.png -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwl/haskell-code-explorer/2f1c2a4c87ebd55b8a335bc4670eec875af8b4c4/javascript/vendor/jquery-ui-1.12.1.custom/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/jquery-ui.structure.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.12.1 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | .ui-draggable-handle { 12 | -ms-touch-action: none; 13 | touch-action: none; 14 | } 15 | /* Layout helpers 16 | ----------------------------------*/ 17 | .ui-helper-hidden { 18 | display: none; 19 | } 20 | .ui-helper-hidden-accessible { 21 | border: 0; 22 | clip: rect(0 0 0 0); 23 | height: 1px; 24 | margin: -1px; 25 | overflow: hidden; 26 | padding: 0; 27 | position: absolute; 28 | width: 1px; 29 | } 30 | .ui-helper-reset { 31 | margin: 0; 32 | padding: 0; 33 | border: 0; 34 | outline: 0; 35 | line-height: 1.3; 36 | text-decoration: none; 37 | font-size: 100%; 38 | list-style: none; 39 | } 40 | .ui-helper-clearfix:before, 41 | .ui-helper-clearfix:after { 42 | content: ""; 43 | display: table; 44 | border-collapse: collapse; 45 | } 46 | .ui-helper-clearfix:after { 47 | clear: both; 48 | } 49 | .ui-helper-zfix { 50 | width: 100%; 51 | height: 100%; 52 | top: 0; 53 | left: 0; 54 | position: absolute; 55 | opacity: 0; 56 | filter:Alpha(Opacity=0); /* support: IE8 */ 57 | } 58 | 59 | .ui-front { 60 | z-index: 100; 61 | } 62 | 63 | 64 | /* Interaction Cues 65 | ----------------------------------*/ 66 | .ui-state-disabled { 67 | cursor: default !important; 68 | pointer-events: none; 69 | } 70 | 71 | 72 | /* Icons 73 | ----------------------------------*/ 74 | .ui-icon { 75 | display: inline-block; 76 | vertical-align: middle; 77 | margin-top: -.25em; 78 | position: relative; 79 | text-indent: -99999px; 80 | overflow: hidden; 81 | background-repeat: no-repeat; 82 | } 83 | 84 | .ui-widget-icon-block { 85 | left: 50%; 86 | margin-left: -8px; 87 | display: block; 88 | } 89 | 90 | /* Misc visuals 91 | ----------------------------------*/ 92 | 93 | /* Overlays */ 94 | .ui-widget-overlay { 95 | position: fixed; 96 | top: 0; 97 | left: 0; 98 | width: 100%; 99 | height: 100%; 100 | } 101 | .ui-resizable { 102 | position: relative; 103 | } 104 | .ui-resizable-handle { 105 | position: absolute; 106 | font-size: 0.1px; 107 | display: block; 108 | -ms-touch-action: none; 109 | touch-action: none; 110 | } 111 | .ui-resizable-disabled .ui-resizable-handle, 112 | .ui-resizable-autohide .ui-resizable-handle { 113 | display: none; 114 | } 115 | .ui-resizable-n { 116 | cursor: n-resize; 117 | height: 7px; 118 | width: 100%; 119 | top: -5px; 120 | left: 0; 121 | } 122 | .ui-resizable-s { 123 | cursor: s-resize; 124 | height: 7px; 125 | width: 100%; 126 | bottom: -5px; 127 | left: 0; 128 | } 129 | .ui-resizable-e { 130 | cursor: e-resize; 131 | width: 7px; 132 | right: -5px; 133 | top: 0; 134 | height: 100%; 135 | } 136 | .ui-resizable-w { 137 | cursor: w-resize; 138 | width: 7px; 139 | left: -5px; 140 | top: 0; 141 | height: 100%; 142 | } 143 | .ui-resizable-se { 144 | cursor: se-resize; 145 | width: 12px; 146 | height: 12px; 147 | right: 1px; 148 | bottom: 1px; 149 | } 150 | .ui-resizable-sw { 151 | cursor: sw-resize; 152 | width: 9px; 153 | height: 9px; 154 | left: -5px; 155 | bottom: -5px; 156 | } 157 | .ui-resizable-nw { 158 | cursor: nw-resize; 159 | width: 9px; 160 | height: 9px; 161 | left: -5px; 162 | top: -5px; 163 | } 164 | .ui-resizable-ne { 165 | cursor: ne-resize; 166 | width: 9px; 167 | height: 9px; 168 | right: -5px; 169 | top: -5px; 170 | } 171 | -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/jquery-ui.structure.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2018-03-18 2 | * http://jqueryui.com 3 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px} -------------------------------------------------------------------------------- /javascript/vendor/jquery-ui-1.12.1.custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-ui", 3 | "title": "jQuery UI", 4 | "description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.", 5 | "version": "1.12.1", 6 | "homepage": "http://jqueryui.com", 7 | "author": { 8 | "name": "jQuery Foundation and other contributors", 9 | "url": "https://github.com/jquery/jquery-ui/blob/1.12.1/AUTHORS.txt" 10 | }, 11 | "main": "ui/widget.js", 12 | "maintainers": [ 13 | { 14 | "name": "Scott González", 15 | "email": "scott.gonzalez@gmail.com", 16 | "url": "http://scottgonzalez.com" 17 | }, 18 | { 19 | "name": "Jörn Zaefferer", 20 | "email": "joern.zaefferer@gmail.com", 21 | "url": "http://bassistance.de" 22 | }, 23 | { 24 | "name": "Mike Sherov", 25 | "email": "mike.sherov@gmail.com", 26 | "url": "http://mike.sherov.com" 27 | }, 28 | { 29 | "name": "TJ VanToll", 30 | "email": "tj.vantoll@gmail.com", 31 | "url": "http://tjvantoll.com" 32 | }, 33 | { 34 | "name": "Felix Nagel", 35 | "email": "info@felixnagel.com", 36 | "url": "http://www.felixnagel.com" 37 | }, 38 | { 39 | "name": "Alex Schmitz", 40 | "email": "arschmitz@gmail.com", 41 | "url": "https://github.com/arschmitz" 42 | } 43 | ], 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/jquery/jquery-ui.git" 47 | }, 48 | "bugs": "https://bugs.jqueryui.com/", 49 | "license": "MIT", 50 | "scripts": { 51 | "test": "grunt" 52 | }, 53 | "dependencies": {}, 54 | "devDependencies": { 55 | "commitplease": "2.3.0", 56 | "grunt": "0.4.5", 57 | "grunt-bowercopy": "1.2.4", 58 | "grunt-cli": "0.1.13", 59 | "grunt-compare-size": "0.4.0", 60 | "grunt-contrib-concat": "0.5.1", 61 | "grunt-contrib-csslint": "0.5.0", 62 | "grunt-contrib-jshint": "0.12.0", 63 | "grunt-contrib-qunit": "1.0.1", 64 | "grunt-contrib-requirejs": "0.4.4", 65 | "grunt-contrib-uglify": "0.11.1", 66 | "grunt-git-authors": "3.1.0", 67 | "grunt-html": "6.0.0", 68 | "grunt-jscs": "2.1.0", 69 | "load-grunt-tasks": "3.4.0", 70 | "rimraf": "2.5.1", 71 | "testswarm": "1.1.0" 72 | }, 73 | "keywords": [] 74 | } 75 | -------------------------------------------------------------------------------- /src/HaskellCodeExplorer/Preprocessor.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE RecordWildCards #-} 2 | {-# LANGUAGE DuplicateRecordFields #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | 5 | module HaskellCodeExplorer.Preprocessor 6 | ( createSourceCodeTransformation 7 | ) where 8 | 9 | import Control.Applicative ((<|>)) 10 | import qualified Data.Attoparsec.Text as AT 11 | import Data.Foldable (foldl') 12 | import qualified Data.HashMap.Strict as HM 13 | import qualified Data.List as L 14 | import qualified Data.Set as S 15 | import qualified Data.Text as T 16 | import HaskellCodeExplorer.Types 17 | ( FileLocation(..) 18 | , HaskellFilePath(..) 19 | , HaskellModulePath(..) 20 | , LinePragma(..) 21 | , SourceCodeTransformation(..) 22 | , haskellPreprocessorExtensions 23 | ) 24 | import System.FilePath (normalise,takeExtension,takeFileName) 25 | 26 | -- | Finds locations of line pragmas and creates an index 27 | createSourceCodeTransformation :: 28 | HaskellModulePath -> T.Text -> T.Text -> (SourceCodeTransformation, T.Text) 29 | createSourceCodeTransformation currentModulePath originalSourceCode sourceCodeAfterPreprocessing = 30 | let sourceCodeLines = T.splitOn "\n" sourceCodeAfterPreprocessing 31 | numberedLines = zip [1 :: Int ..] sourceCodeLines 32 | currentFilePath = 33 | HaskellFilePath . getHaskellModulePath $ currentModulePath 34 | addPragma :: [LinePragma] -> (Int, T.Text) -> [LinePragma] 35 | addPragma acc (lineNumber, line) = 36 | case AT.parseOnly linePragmaParser line of 37 | Right (originalLineNumber, mbFileName) -> 38 | LinePragma 39 | (maybe 40 | currentFilePath 41 | (HaskellFilePath . T.pack . normalise . T.unpack) 42 | mbFileName) 43 | lineNumber 44 | originalLineNumber : 45 | acc 46 | Left _ -> acc 47 | totalLines = length numberedLines 48 | pragmas = L.reverse . L.foldl' addPragma [] $ numberedLines 49 | pragmaPath = filePath :: LinePragma -> HaskellFilePath 50 | currentFileExtension = 51 | takeExtension . T.unpack . getHaskellFilePath $ currentFilePath 52 | standardHeaderFiles = 53 | [ "stdc-predef.h" 54 | , "cabal_macros.h" 55 | , "ghcversion.h" 56 | , "HsVersions.h" 57 | , "ghc_boot_platform.h" 58 | , "ghcautoconf.h" 59 | ] 60 | hasIncludedFiles = 61 | L.any 62 | ((\path -> 63 | let fileName = takeFileName . T.unpack . getHaskellFilePath $ path 64 | in (path /= currentFilePath) && 65 | (path /= HaskellFilePath "") && 66 | (path /= HaskellFilePath "") && 67 | not ("ghc_" `L.isPrefixOf` fileName) && 68 | (fileName `notElem` standardHeaderFiles)) . 69 | pragmaPath) 70 | pragmas 71 | in if hasIncludedFiles || 72 | currentFileExtension `elem` haskellPreprocessorExtensions 73 | then ( SourceCodeTransformation 74 | totalLines 75 | currentModulePath 76 | (S.fromList pragmas) 77 | (indexLocations totalLines currentFilePath pragmas) 78 | , sourceCodeAfterPreprocessing) 79 | else ( SourceCodeTransformation 80 | (length $ T.splitOn "\n" originalSourceCode) 81 | currentModulePath 82 | S.empty 83 | HM.empty 84 | , originalSourceCode) 85 | 86 | -- | Parses line pragma 87 | linePragmaParser :: AT.Parser (Int, Maybe T.Text) 88 | linePragmaParser = pragma1 <|> pragma2 89 | where 90 | pragma1 :: AT.Parser (Int, Maybe T.Text) 91 | pragma1 = parser "#" "line" 92 | 93 | pragma2 :: AT.Parser (Int, Maybe T.Text) 94 | pragma2 = parser "{-#" "LINE" 95 | 96 | parser :: T.Text -> T.Text -> AT.Parser (Int, Maybe T.Text) 97 | parser start line = do 98 | _ <- AT.string start 99 | _ <- AT.takeWhile (== ' ') 100 | _ <- AT.string line <|> return "" 101 | _ <- AT.takeWhile (== ' ') 102 | num <- AT.decimal 103 | _ <- AT.takeWhile (== ' ') 104 | mbName <- (Just <$> fileName) <|> return Nothing 105 | return (num, mbName) 106 | 107 | fileName :: AT.Parser T.Text 108 | fileName = AT.string "\"" *> AT.takeTill (== '\"') <* AT.string "\"" 109 | 110 | data Line = FirstLine | LastLine Int | Pragma LinePragma deriving (Show,Eq) 111 | 112 | -- | Creates a HashMap whose keys are filenames and values are locations in a 113 | -- preprocessed source code 114 | indexLocations :: 115 | Int 116 | -> HaskellFilePath 117 | -> [LinePragma] 118 | -> HM.HashMap HaskellFilePath (S.Set FileLocation) 119 | indexLocations totalLines preprocessedFilePath pragmas = 120 | foldl' add HM.empty . (zip <*> tail) $ 121 | (FirstLine : map Pragma pragmas) ++ [LastLine totalLines] 122 | where 123 | add :: 124 | HM.HashMap HaskellFilePath (S.Set FileLocation) 125 | -> (Line, Line) 126 | -> HM.HashMap HaskellFilePath (S.Set FileLocation) 127 | -- Interval between the first line and the first pragma 128 | add hMap (FirstLine, Pragma LinePragma {..}) 129 | | lineNumberPreprocessed > 1 = 130 | HM.insertWith 131 | S.union 132 | preprocessedFilePath 133 | (S.singleton (FileLocation 1 lineNumberPreprocessed 0)) 134 | hMap 135 | | otherwise = hMap 136 | -- Interval between two pragmas 137 | add hMap (Pragma (LinePragma fileName lineNumberPreprocessed1 lineNumberOriginal1), 138 | Pragma (LinePragma _ lineNumberPreprocessed2 _)) 139 | | lineNumberPreprocessed2 - lineNumberPreprocessed1 > 1 = 140 | HM.insertWith 141 | S.union 142 | fileName 143 | (S.singleton 144 | (FileLocation 145 | lineNumberOriginal1 146 | (lineNumberOriginal1 + 147 | (lineNumberPreprocessed2 - lineNumberPreprocessed1 - 2)) 148 | (lineNumberPreprocessed1 - lineNumberOriginal1 + 1))) 149 | hMap 150 | | otherwise = hMap 151 | -- Interval between the last pragma and the last line 152 | add hMap (Pragma (LinePragma fileName lineNumberPreprocessed lineNumberOriginal), 153 | LastLine lastLineNumberPreprocessed) 154 | | lastLineNumberPreprocessed - lineNumberPreprocessed > 1 = 155 | HM.insertWith 156 | S.union 157 | fileName 158 | (S.singleton 159 | (FileLocation 160 | lineNumberOriginal 161 | (lineNumberOriginal + (lastLineNumberPreprocessed - lineNumberPreprocessed - 2)) 162 | (lineNumberPreprocessed - lineNumberOriginal + 1))) 163 | hMap 164 | | otherwise = hMap 165 | add hMap _ = hMap 166 | -------------------------------------------------------------------------------- /stack-8.0.2.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-9.14 2 | packages: 3 | - '.' 4 | extra-deps: 5 | - servant-0.12.1 6 | - servant-server-0.12 7 | - cabal-helper-0.8.0.2 8 | -------------------------------------------------------------------------------- /stack-8.2.2.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-11.3 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | extra-deps: 6 | - cabal-plan-0.4.0.0 7 | - pretty-show-1.8.2 8 | -------------------------------------------------------------------------------- /stack-8.4.3.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-12.12 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - cabal-plan-0.4.0.0 8 | -------------------------------------------------------------------------------- /stack-8.4.4.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-12.16 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - cabal-plan-0.4.0.0 8 | -------------------------------------------------------------------------------- /stack-8.6.3.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.0 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - cabal-plan-0.4.0.0 8 | -------------------------------------------------------------------------------- /stack-8.6.4.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.12 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - cabal-plan-0.4.0.0 8 | -------------------------------------------------------------------------------- /stack-8.6.5.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-14.1 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - cabal-plan-0.4.0.0 8 | -------------------------------------------------------------------------------- /stack-8.8.3.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-15.4 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - haddock-library-1.7.0@sha256:8f230ebb680b559256d9c18ce4942ba5bf220b167804b4ebd5cc9e47cc4973cd 8 | - cabal-install-3.0.0.0@sha256:5e3c4376e53c06521cca2c037074423dbb967e228af6174bf842ff38aa82b035 9 | - git: https://github.com/haskell/hackage-security 10 | commit: 2057cdf2abba852881cd9d055c96f03cd8c829e7 11 | subdirs: 12 | - hackage-security 13 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-14.1 2 | packages: 3 | - '.' 4 | - 'vendor/cabal-helper-0.8.1.2' 5 | allow-newer: true 6 | extra-deps: 7 | - cabal-plan-0.4.0.0 8 | -------------------------------------------------------------------------------- /test/data/File.hs: -------------------------------------------------------------------------------- 1 | 1 File.hs 2 | 2 File.hs 3 | 3 File.hs 4 | 4 File.hs 5 | 5 File.hs 6 | 6 File.hs 7 | 7 File.hs 8 | #include "File1.hs" 9 | 9 File.hs 10 | #if 0 11 | 11 File.hs 12 | 12 File.hs 13 | 13 File.hs 14 | 14 File.hs 15 | 15 File.hs 16 | 16 File.hs 17 | #endif 18 | 18 File.hs 19 | 19 File.hs 20 | #include "File3.hs" 21 | 21 File.hs 22 | -------------------------------------------------------------------------------- /test/data/File1.hs: -------------------------------------------------------------------------------- 1 | 1 File1.hs 2 | 2 File1.hs 3 | #if 0 4 | 4 File1.hs 5 | 5 File1.hs 6 | 6 File1.hs 7 | 7 File1.hs 8 | 8 File1.hs 9 | 9 File1.hs 10 | 10 File1.hs 11 | 11 File1.hs 12 | 12 File1.hs 13 | #endif 14 | #include "File2.hs" 15 | 15 File1.hs 16 | 16 File1.hs 17 | -------------------------------------------------------------------------------- /test/data/File2.hs: -------------------------------------------------------------------------------- 1 | 1 File2.hs 2 | 2 File2.hs 3 | 3 File2.hs 4 | 4 File2.hs 5 | 5 File2.hs 6 | -------------------------------------------------------------------------------- /test/data/File3.hs: -------------------------------------------------------------------------------- 1 | 1 File3.hs 2 | 2 File3.hs 3 | 3 File3.hs 4 | -------------------------------------------------------------------------------- /test/data/FileAfterPreprocessor.hs: -------------------------------------------------------------------------------- 1 | 1 File.hs 2 | 2 File.hs 3 | 3 File.hs 4 | 4 File.hs 5 | 5 File.hs 6 | 6 File.hs 7 | 7 File.hs 8 | # 1 "File1.hs" 1 9 | 1 File1.hs 10 | 2 File1.hs 11 | # 14 "File1.hs" 12 | # 1 "File2.hs" 1 13 | 1 File2.hs 14 | 2 File2.hs 15 | 3 File2.hs 16 | 4 File2.hs 17 | 5 File2.hs 18 | # 15 "File1.hs" 2 19 | 15 File1.hs 20 | 16 File1.hs 21 | # 9 "File.hs" 2 22 | 9 File.hs 23 | # 18 "File.hs" 24 | 18 File.hs 25 | 19 File.hs 26 | # 1 "File3.hs" 1 27 | 1 File3.hs 28 | 2 File3.hs 29 | 3 File3.hs 30 | # 21 "File.hs" 2 31 | 21 File.hs 32 | -------------------------------------------------------------------------------- /test/test-package/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | .stack-work/ 4 | .haskell-code-explorer/ 5 | .cabal-sandbox/ 6 | cabal.sandbox.config 7 | cabal.config 8 | log/ 9 | tmp/ 10 | TAGS 11 | \#*\# 12 | .\#* 13 | -------------------------------------------------------------------------------- /test/test-package/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /test/test-package/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Lib 4 | 5 | main :: IO () 6 | main = someFunc 7 | 8 | -------------------------------------------------------------------------------- /test/test-package/src/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib 2 | ( someFunc 3 | ) where 4 | 5 | import Types(Test(..)) 6 | 7 | -- | someFunc documentation 8 | someFunc :: IO () 9 | someFunc = putStrLn "someFunc" 10 | 11 | -- | mkTest documentation 12 | mkTest :: Int -> Test 13 | mkTest i = Test i 14 | -------------------------------------------------------------------------------- /test/test-package/src/Types.hs: -------------------------------------------------------------------------------- 1 | module Types 2 | ( Test(..) 3 | ) where 4 | 5 | data Test = Test Int 6 | -------------------------------------------------------------------------------- /test/test-package/stack-8.0.2.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-9.14 2 | -------------------------------------------------------------------------------- /test/test-package/stack-8.2.2.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-11.3 2 | -------------------------------------------------------------------------------- /test/test-package/stack-8.4.3.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-12.12 2 | -------------------------------------------------------------------------------- /test/test-package/stack-8.4.4.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-12.16 2 | -------------------------------------------------------------------------------- /test/test-package/stack-8.6.3.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.0 2 | -------------------------------------------------------------------------------- /test/test-package/stack-8.6.4.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.12 2 | -------------------------------------------------------------------------------- /test/test-package/stack-8.6.5.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.20 2 | -------------------------------------------------------------------------------- /test/test-package/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.20 2 | -------------------------------------------------------------------------------- /test/test-package/test-package.cabal: -------------------------------------------------------------------------------- 1 | name: test-package 2 | version: 0.1.0.0 3 | description: Test package 4 | build-type: Simple 5 | cabal-version: >= 1.10 6 | library 7 | hs-source-dirs: 8 | src 9 | build-depends: 10 | base >=4.7 && <5 11 | exposed-modules: 12 | Lib 13 | other-modules: 14 | Types 15 | Paths_test_package 16 | default-language: Haskell2010 17 | executable test-package-exe 18 | main-is: Main.hs 19 | hs-source-dirs: 20 | app 21 | build-depends: 22 | base >=4.7 && <5 23 | , test-package 24 | default-language: Haskell2010 25 | test-suite test-package-test 26 | type: exitcode-stdio-1.0 27 | main-is: Spec.hs 28 | hs-source-dirs: 29 | test 30 | build-depends: 31 | base >=4.7 && <5 32 | , test-package 33 | default-language: Haskell2010 34 | -------------------------------------------------------------------------------- /test/test-package/test/Spec.hs: -------------------------------------------------------------------------------- 1 | main :: IO () 2 | main = putStrLn "Test suite not yet implemented" 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | *~ 3 | /.cabal-sandbox/ 4 | add-source-timestamps 5 | package.cache 6 | cabal.sandbox.config 7 | # Mac OS generates 8 | # .DS_Store 9 | *.o 10 | *.dyn_o 11 | *.hi 12 | *.dyn_hi 13 | 14 | # Emacs lock files 15 | .#* 16 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | 4 | job-ghc8.4.3-cabal-install2.2.0.0: 5 | image: registry.gitlab.com/dxld/ghc-mod:ghc8.4.3-cabal-install2.2.0.0 6 | stage: build 7 | script: "$CI_PROJECT_DIR/scripts/ci/build.sh" 8 | 9 | job-ghc8.2.2-cabal-install2.0.0.0: 10 | image: registry.gitlab.com/dxld/ghc-mod:ghc8.2.2-cabal-install2.0.0.0 11 | stage: build 12 | script: "$CI_PROJECT_DIR/scripts/ci/build.sh" 13 | 14 | job-ghc8.0.2-cabal-install2.0.0.0: 15 | image: registry.gitlab.com/dxld/ghc-mod:ghc8.0.2-cabal-install2.0.0.0 16 | stage: build 17 | script: "$CI_PROJECT_DIR/scripts/ci/build.sh" 18 | 19 | job-ghc7.10.3-cabal-install2.0.0.0: 20 | image: registry.gitlab.com/dxld/ghc-mod:ghc7.10.3-cabal-install2.0.0.0 21 | stage: build 22 | script: "$CI_PROJECT_DIR/scripts/ci/build.sh" 23 | 24 | job-ghc7.8.4-cabal-install2.0.0.0: 25 | image: registry.gitlab.com/dxld/ghc-mod:ghc7.8.4-cabal-install2.0.0.0 26 | stage: build 27 | script: "$CI_PROJECT_DIR/scripts/ci/build.sh" 28 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: haskell 2 | ghc: 3 | - 7.4 4 | - 7.6 5 | - 7.8 6 | 7 | sudo: false 8 | 9 | addons: 10 | apt: 11 | packages: 12 | - zlib1g-dev 13 | 14 | cache: 15 | apt: true 16 | directories: 17 | - ~/.cabal 18 | - ~/.ghc 19 | - ~/.stack 20 | 21 | install: 22 | - export HOME=/tmp 23 | - cabal update 24 | - echo $PATH 25 | - which cabal 26 | - cabal install -j --only-dependencies --enable-tests 27 | - if [ -n "$(ghc --version | awk '{ print $8 }' | sed -n '/^7.4/p')" ]; then cabal install Cabal --constraint "Cabal == 1.16.*"; fi 28 | 29 | 30 | before_script: 31 | - rm -f ~/.ghc-mod cabal-helper-*-Cabal-* 32 | 33 | script: 34 | - touch ChangeLog # Create ChangeLog if we're not on the release branch 35 | - cabal check 36 | 37 | - cabal sdist 38 | - export SRC_TGZ="$PWD/dist/$(cabal info . | awk '{print $2 ".tar.gz";exit}')" 39 | - rm -rf /tmp/cabal-helper* && cd /tmp 40 | - tar -xf $SRC_TGZ && cd cabal-helper*/ 41 | 42 | - if [ -n "$(ghc --version | awk '{ print $8 }' | sed -n '/^7.8/p')" ]; then export WERROR="--ghc-option=-Werror"; fi 43 | - cabal configure --enable-tests $WERROR 44 | - cabal build 45 | - ./dist/build/cabal-helper-wrapper-v0.7/cabal-helper-wrapper-v0.7 . dist "compiler-version" "entrypoints" "source-dirs" "ghc-options" "ghc-src-options" "ghc-pkg-options" "ghc-lang-options" 46 | - ./dist/build/spec/spec 47 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/README.md: -------------------------------------------------------------------------------- 1 | # cabal-helper 2 | [![build status](https://gitlab.com/dxld/cabal-helper/badges/master/build.svg)](https://gitlab.com/dxld/cabal-helper/commits/master) 3 | 4 | Cabal's little helper provides access to build information gathered by `cabal` 5 | when configuring a project. Specifically we're interested in retrieving enough 6 | information to bring up a compiler session, using the GHC API, which is similar 7 | to running `cabal repl` in a project. 8 | 9 | While simple in principle this is complicated by the fact that the information 10 | Cabal writes to disk is in an unstable format and only really accessible through 11 | the Cabal API itself. 12 | 13 | Since we do not want to bind the user of a development tool which utilises this 14 | library to a specific version of Cabal we compile the code which interfaces with 15 | the Cabal library's API on the user's machine, at runtime, against whichever 16 | version of Cabal was used to write the on disk information for a given project. 17 | 18 | If this version of Cabal is not available on the users machine anymore, which is 19 | fairly likely since cabal-install is usually linked statically, we have support 20 | for compiling the Cabal library also. In this case the library is installed into 21 | a private, isolated, package database in `$XDG_CACHE_HOME/cabal-helper` so as to 22 | not interfere with the user's package database. 23 | 24 | ## IRC 25 | 26 | If you have any problems, suggestions, comments swing by 27 | [\#ghc-mod (web client)](https://kiwiirc.com/client/irc.freenode.org/ghc-mod) on 28 | Freenode. If you're reporting a bug please also create an issue 29 | [here](https://github.com/DanielG/cabal-helper/issues) so we have a way to 30 | contact you if you don't have time to stay. 31 | 32 | Do hang around for a while if no one answers and repeat your question if you 33 | still haven't gotten any answer after a day or so. You're most likely to get an 34 | answer during the day in GMT+1. 35 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/bump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -z "$1" ]; then 6 | echo "Usage: $0 VERSION" >&2 7 | exit 1 8 | fi 9 | 10 | VERSION=$1 11 | 12 | if ! echo $VERSION | grep "^[0-9.]"; then 13 | echo "invalid version"; 14 | exit 1 15 | fi 16 | 17 | cd $(dirname $0)/.. 18 | 19 | sed -r -i 's/^(version:[[:space:]]*)[0-9.]+/\1'"$VERSION"'/' cabal-helper.cabal 20 | 21 | git add cabal-helper.cabal 22 | git commit -m "Bump version to $VERSION" 23 | 24 | git tag "v$VERSION" 25 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | CI_SCRIPTS_DIR="$(realpath "$(dirname "$0")")" 6 | 7 | for step in $(printf '%s\n' "$CI_SCRIPTS_DIR"/steps/* | sort); do 8 | . $step 9 | done 10 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/print-packages.sh: -------------------------------------------------------------------------------- 1 | if [ -e cabal.sandbox.config ]; then 2 | cabal sandbox hc-pkg list 3 | else 4 | ghc-pkg list 5 | fi 6 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/retry.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MIT LICENSE 4 | # 5 | # Copyright (c) 2016 Travis CI GmbH 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # 25 | # 26 | # Copied from github.com/travis-ci/travis-build and modified to pass shellcheck. 27 | # Copied from https://github.com/cockroachdb/cockroach/blob/b4b7412d1c899a7488a9839eb4e01a616e4de135/scripts/travis_retry.sh and modified slightly 28 | 29 | retry() { 30 | local result=0 31 | local count=1 32 | while [ $count -le 3 ]; do 33 | [ $result -ne 0 ] && { 34 | echo -e "\n${ANSI_RED}The command \"$*\" failed. Retrying, $count of 3.${ANSI_RESET}\n" >&2 35 | } 36 | "$@" 37 | result=$? 38 | [ $result -eq 0 ] && break 39 | count=$((count + 1)) 40 | sleep 10 41 | done 42 | 43 | [ $count -gt 3 ] && { 44 | echo -e "\n${ANSI_RED}The command \"$*\" failed 3 times.${ANSI_RESET}\n" >&2 45 | } 46 | 47 | return $result 48 | } 49 | 50 | retry "$@" 51 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/00-config.sh: -------------------------------------------------------------------------------- 1 | if [ -w . ]; then 2 | sandbox="$PWD"/.cabal-sandbox 3 | sandbox_config="$PWD"/cabal.sandbox.config 4 | else 5 | sandbox="$HOME"/cabal-sandbox 6 | sandbox_config="$HOME"/cabal.sandbox.config 7 | fi 8 | 9 | source_dir="$(mktemp --tmpdir -d "cabal-helper.sdistXXXXXXXXX")" 10 | build_dir="$(mktemp --tmpdir -d "cabal-helper.distXXXXXXXXX")" 11 | 12 | NPROC=${NPROC:-1} 13 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/05-print-packages.sh: -------------------------------------------------------------------------------- 1 | ../print-packages.sh -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/10-dependencies.sh: -------------------------------------------------------------------------------- 1 | "$CI_SCRIPTS_DIR"/retry.sh cabal update 2 | cabal --sandbox-config="$sandbox_config" sandbox init --sandbox="$sandbox" 3 | cabal --sandbox-config="$sandbox_config" install --only-dependencies --enable-tests 4 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/15-print-packages.sh: -------------------------------------------------------------------------------- 1 | ../print-packages.sh -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/20-sdist.sh: -------------------------------------------------------------------------------- 1 | mkdir -p "$source_dir" 2 | mkdir -p "$build_dir" 3 | 4 | cabal --sandbox-config="$sandbox_config" sdist --builddir="$build_dir" --output-directory="$source_dir" 5 | 6 | cd "$source_dir" 7 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/30-build.sh: -------------------------------------------------------------------------------- 1 | # -fdev enables building the helper "main" exe directly and enables more warnings 2 | cabal --sandbox-config="$sandbox_config" configure --builddir="$build_dir" --enable-tests -fdev 3 | cabal --sandbox-config="$sandbox_config" build --builddir="$build_dir" 4 | cabal --sandbox-config="$sandbox_config" haddock --builddir="$build_dir" 5 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/ci/steps/40-test.sh: -------------------------------------------------------------------------------- 1 | cabal_helper_libexecdir="$build_dir"/build/cabal-helper-wrapper \ 2 | cabal --sandbox-config="$sandbox_config" test --builddir="$build_dir" --show-details=streaming 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/scripts/update-cabal-common-section-instantiations.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | delete sections; 3 | section=""; 4 | ignoring=0; 5 | } 6 | 7 | /^[[:space:]]*$/ { 8 | section=""; 9 | ignoring=0; 10 | } 11 | 12 | { 13 | if(section) { 14 | tmp = sections[section]; 15 | sections[section] = tmp (tmp ? RS : "") $0; 16 | } 17 | } 18 | 19 | /^[[:space:]]*-- *Common/ { 20 | section = $3 21 | } 22 | 23 | /^[[:space:]]*-- *Instantiate *common/ { 24 | ignoring=1 25 | 26 | print $0; 27 | print sections[$4]; 28 | } 29 | 30 | { 31 | if(!ignoring) { 32 | print $0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Compiletime/Compat/Environment.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2017 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE CPP #-} 18 | module CabalHelper.Compiletime.Compat.Environment where 19 | 20 | import qualified System.Environment 21 | #ifndef mingw32_HOST_OS 22 | import qualified System.Posix.Env (setEnv) 23 | #endif 24 | 25 | lookupEnv :: String -> IO (Maybe String) 26 | lookupEnv var = 27 | do env <- System.Environment.getEnvironment 28 | return (lookup var env) 29 | 30 | setEnv :: String -> String -> IO () 31 | #ifdef mingw32_HOST_OS 32 | setEnv = System.Environment.setEnv 33 | #else 34 | setEnv k v = System.Posix.Env.setEnv k v True 35 | #endif 36 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Compiletime/Compat/ProgramDb.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2018 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE CPP #-} 18 | module CabalHelper.Compiletime.Compat.ProgramDb 19 | ( defaultProgramDb 20 | , programPath 21 | , lookupProgram 22 | , ghcProgram 23 | , ghcPkgProgram 24 | ) where 25 | 26 | import Distribution.Simple.Program 27 | 28 | #if !MIN_VERSION_Cabal(2,0,0) 29 | defaultProgramDb = defaultProgramConfiguration 30 | #endif 31 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Compiletime/Compat/Version.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2017-2018 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE CPP #-} 18 | module CabalHelper.Compiletime.Compat.Version 19 | ( DataVersion 20 | , toDataVersion 21 | , fromDataVersion 22 | , Data.Version.showVersion 23 | , makeDataVersion 24 | ) where 25 | 26 | import qualified Data.Version 27 | import qualified Distribution.Version (Version) 28 | #if MIN_VERSION_Cabal(2,0,0) 29 | import qualified Distribution.Version (versionNumbers, mkVersion) 30 | #endif 31 | 32 | type DataVersion = Data.Version.Version 33 | 34 | toDataVersion :: Distribution.Version.Version -> Data.Version.Version 35 | fromDataVersion :: Data.Version.Version -> Distribution.Version.Version 36 | #if MIN_VERSION_Cabal(2,0,0) 37 | toDataVersion v = Data.Version.Version (Distribution.Version.versionNumbers v) [] 38 | fromDataVersion (Data.Version.Version vs _) = Distribution.Version.mkVersion vs 39 | #else 40 | toDataVersion = id 41 | fromDataVersion = id 42 | #endif 43 | 44 | makeDataVersion :: [Int] -> Data.Version.Version 45 | #if MIN_VERSION_base(4,8,0) 46 | makeDataVersion = Data.Version.makeVersion 47 | #else 48 | makeDataVersion xs = Data.Version.Version xs [] 49 | #endif 50 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Compiletime/Data.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2015-2017 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} 18 | {-# OPTIONS_GHC -fforce-recomp #-} 19 | 20 | {-| 21 | Module : CabalHelper.Compiletime.Data 22 | Description : Embeds source code for runtime component using TH 23 | License : GPL-3 24 | -} 25 | 26 | module CabalHelper.Compiletime.Data where 27 | 28 | import Control.Monad 29 | import Control.Monad.IO.Class 30 | import Data.Functor 31 | import qualified Data.ByteString as BS 32 | import qualified Data.ByteString.UTF8 as UTF8 33 | import Language.Haskell.TH 34 | import System.Directory 35 | import System.FilePath 36 | import System.IO.Temp 37 | import System.PosixCompat.Files 38 | import System.PosixCompat.Time 39 | import System.PosixCompat.Types 40 | import Prelude 41 | 42 | import CabalHelper.Compiletime.Compat.Environment 43 | 44 | withSystemTempDirectoryEnv :: String -> (FilePath -> IO b) -> IO b 45 | withSystemTempDirectoryEnv tpl f = do 46 | m <- liftIO $ lookupEnv "CABAL_HELPER_KEEP_SOURCEDIR" 47 | case m of 48 | Nothing -> withSystemTempDirectory tpl f 49 | Just _ -> do 50 | tmpdir <- getCanonicalTemporaryDirectory 51 | f =<< createTempDirectory tmpdir tpl 52 | 53 | createHelperSources :: FilePath -> IO () 54 | createHelperSources dir = do 55 | let chdir = dir "CabalHelper" 56 | liftIO $ do 57 | createDirectoryIfMissing True $ chdir "Runtime" 58 | createDirectoryIfMissing True $ chdir "Shared" 59 | 60 | let modtime :: EpochTime 61 | modtime = fromIntegral $ (read :: String -> Integer) 62 | -- See https://reproducible-builds.org/specs/source-date-epoch/ 63 | $(runIO $ do 64 | msde :: Maybe Integer 65 | <- fmap read <$> lookupEnv "SOURCE_DATE_EPOCH" 66 | (current_time :: Integer) <- round . toRational <$> epochTime 67 | return $ LitE . StringL $ show $ maybe current_time id msde) 68 | 69 | liftIO $ forM_ sourceFiles $ \(fn, src) -> do 70 | let path = chdir fn 71 | BS.writeFile path $ UTF8.fromString src 72 | setFileTimes path modtime modtime 73 | 74 | 75 | sourceFiles :: [(FilePath, String)] 76 | sourceFiles = 77 | [ ("Runtime/Main.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Runtime/Main.hs"))) 78 | , ("Shared/Common.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Shared/Common.hs"))) 79 | , ("Shared/Sandbox.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Shared/Sandbox.hs"))) 80 | , ("Shared/InterfaceTypes.hs", $(LitE . StringL <$> runIO (UTF8.toString <$> BS.readFile "src/CabalHelper/Shared/InterfaceTypes.hs"))) 81 | ] 82 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Compiletime/Log.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2017-2018 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE ScopedTypeVariables #-} 18 | 19 | {-| 20 | Module : CabalHelper.Compiletime.Log 21 | Description : Basic logging facilities 22 | License : GPL-3 23 | -} 24 | 25 | module CabalHelper.Compiletime.Log where 26 | 27 | import Control.Monad 28 | import Control.Monad.IO.Class 29 | import Control.Exception as E 30 | import Data.String 31 | import System.IO 32 | import Prelude 33 | 34 | import CabalHelper.Compiletime.Types 35 | 36 | vLog :: MonadIO m => Options -> String -> m () 37 | vLog Options { oVerbose = True } msg = 38 | liftIO $ hPutStrLn stderr msg 39 | vLog _ _ = return () 40 | 41 | logIOError :: Options -> String -> IO (Maybe a) -> IO (Maybe a) 42 | logIOError opts label a = do 43 | a `E.catch` \(ex :: IOError) -> do 44 | vLog opts $ label ++ ": " ++ show ex 45 | return Nothing 46 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Compiletime/Types.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2015-2018 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures #-} 18 | 19 | {-| 20 | Module : CabalHelper.Compiletime.Types 21 | Description : Types used throughout 22 | License : GPL-3 23 | -} 24 | 25 | module CabalHelper.Compiletime.Types where 26 | 27 | import Data.Version 28 | 29 | data Options = Options { 30 | oHelp :: Bool 31 | , oVerbose :: Bool 32 | , oGhcProgram :: FilePath 33 | , oGhcPkgProgram :: FilePath 34 | , oCabalProgram :: FilePath 35 | , oCabalVersion :: Maybe Version 36 | , oCabalPkgDb :: Maybe PackageDbDir 37 | } 38 | 39 | newtype PackageDbDir = PackageDbDir { unPackageDbDir :: FilePath } 40 | 41 | defaultOptions :: Options 42 | defaultOptions = Options False False "ghc" "ghc-pkg" "cabal" Nothing Nothing 43 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Shared/Common.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2015-2018 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-| 18 | Module : CabalHelper.Shared.Common 19 | Description : Shared utility functions 20 | License : GPL-3 21 | -} 22 | 23 | {-# LANGUAGE CPP, DeriveDataTypeable, OverloadedStrings #-} 24 | module CabalHelper.Shared.Common where 25 | 26 | #ifdef MIN_VERSION_Cabal 27 | #undef CH_MIN_VERSION_Cabal 28 | #define CH_MIN_VERSION_Cabal MIN_VERSION_Cabal 29 | #endif 30 | 31 | import Control.Applicative 32 | import Control.Exception as E 33 | import Control.Monad 34 | import Data.Char 35 | import Data.List 36 | import Data.Maybe 37 | import Data.Version 38 | import Data.Typeable 39 | import Data.ByteString (ByteString) 40 | import qualified Data.ByteString as BS 41 | import qualified Data.ByteString.Char8 as BS8 42 | #if CH_MIN_VERSION_Cabal(2,2,0) 43 | import qualified Distribution.PackageDescription.Parsec as P 44 | #else 45 | import qualified Distribution.PackageDescription.Parse as P 46 | #endif 47 | import System.Environment 48 | import System.IO 49 | import qualified System.Info 50 | import System.Exit 51 | import System.Directory 52 | import System.FilePath 53 | import Text.ParserCombinators.ReadP 54 | import Prelude 55 | 56 | data Panic = Panic String deriving (Typeable, Show) 57 | instance Exception Panic 58 | 59 | panic :: String -> a 60 | panic msg = throw $ Panic msg 61 | 62 | panicIO :: String -> IO a 63 | panicIO msg = throwIO $ Panic msg 64 | 65 | handlePanic :: IO a -> IO a 66 | handlePanic action = 67 | action `E.catch` \(Panic msg) -> errMsg msg >> exitFailure 68 | 69 | errMsg :: String -> IO () 70 | errMsg str = do 71 | prog <- getProgName 72 | hPutStrLn stderr $ prog ++ ": " ++ str 73 | 74 | -- | @getCabalConfigHeader "dist/setup-config"@ returns the cabal version and 75 | -- compiler version 76 | getCabalConfigHeader :: FilePath -> IO (Maybe (Version, (ByteString, Version))) 77 | getCabalConfigHeader file = bracket (openFile file ReadMode) hClose $ \h -> do 78 | parseHeader <$> BS.hGetLine h 79 | 80 | parseHeader :: ByteString -> Maybe (Version, (ByteString, Version)) 81 | parseHeader header = case BS8.words header of 82 | ["Saved", "package", "config", "for", _pkgId , 83 | "written", "by", cabalId, 84 | "using", compId] 85 | -> liftM2 (,) (snd <$> parsePkgId cabalId) (parsePkgId compId) 86 | _ -> Nothing 87 | 88 | parsePkgId :: ByteString -> Maybe (ByteString, Version) 89 | parsePkgId bs = 90 | case BS8.split '-' bs of 91 | [pkg, vers] -> Just (pkg, parseVer $ BS8.unpack vers) 92 | _ -> Nothing 93 | 94 | parseVer :: String -> Version 95 | parseVer vers = runReadP parseVersion vers 96 | 97 | trim :: String -> String 98 | trim = dropWhileEnd isSpace 99 | 100 | majorVer :: Version -> Version 101 | majorVer (Version b _) = Version (take 2 b) [] 102 | 103 | sameMajorVersionAs :: Version -> Version -> Bool 104 | sameMajorVersionAs a b = majorVer a == majorVer b 105 | 106 | runReadP :: ReadP t -> String -> t 107 | runReadP p i = case filter ((=="") . snd) $ readP_to_S p i of 108 | (a,""):[] -> a 109 | _ -> error $ "Error parsing: " ++ show i 110 | 111 | appCacheDir :: IO FilePath 112 | appCacheDir = 113 | ( "cabal-helper") <$> getEnvDefault "XDG_CACHE_HOME" (homeRel cache) 114 | where 115 | -- for GHC 7.4 116 | lookupEnv' var = do env <- getEnvironment; return (lookup var env) 117 | getEnvDefault var def = lookupEnv' var >>= \m -> case m of Nothing -> def; Just x -> return x 118 | homeRel path = ( path) <$> getHomeDirectory 119 | cache = 120 | case System.Info.os of 121 | "mingw32" -> windowsCache 122 | _ -> unixCache 123 | 124 | windowsCache = "Local Settings" "Cache" 125 | unixCache = ".cache" 126 | 127 | isCabalFile :: FilePath -> Bool 128 | isCabalFile f = takeExtension' f == ".cabal" 129 | 130 | takeExtension' :: FilePath -> String 131 | takeExtension' p = 132 | if takeFileName p == takeExtension p 133 | then "" -- just ".cabal" is not a valid cabal file 134 | else takeExtension p 135 | 136 | replace :: String -> String -> String -> String 137 | replace n r hs' = go "" hs' 138 | where 139 | go acc h 140 | | take (length n) h == n = 141 | reverse acc ++ r ++ drop (length n) h 142 | go acc (h:hs) = go (h:acc) hs 143 | go acc [] = reverse acc 144 | 145 | 146 | #if CH_MIN_VERSION_Cabal(2,2,0) 147 | readPackageDescription = P.readGenericPackageDescription 148 | #else 149 | readPackageDescription = P.readPackageDescription 150 | #endif 151 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Shared/InterfaceTypes.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2015-2018 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-# LANGUAGE DeriveGeneric, DeriveDataTypeable, DefaultSignatures #-} 18 | 19 | {-| 20 | Module : CabalHelper.Shared.InterfaceTypes 21 | Description : Types which are used by c-h library and executable to communicate 22 | License : GPL-3 23 | 24 | These types are used to communicate between the cabal-helper library and main 25 | executable, using Show/Read. If any types in this module change the major 26 | version must be bumped since this will be exposed in the @Distribution.Helper@ 27 | module. 28 | 29 | The cached executables in @$XDG_CACHE_HOME/cabal-helper@ use the cabal-helper 30 | version (among other things) as a cache key so we don't need to worry about 31 | talking to an old executable. 32 | -} 33 | module CabalHelper.Shared.InterfaceTypes where 34 | 35 | import GHC.Generics 36 | import Data.Version 37 | 38 | data ChResponse 39 | = ChResponseCompList [(ChComponentName, [String])] 40 | | ChResponseEntrypoints [(ChComponentName, ChEntrypoint)] 41 | | ChResponseNeedsBuild [(ChComponentName, NeedsBuildOutput)] 42 | | ChResponseList [String] 43 | | ChResponsePkgDbs [ChPkgDb] 44 | | ChResponseLbi String 45 | | ChResponseVersion String Version 46 | | ChResponseLicenses [(String, [(String, Version)])] 47 | | ChResponseFlags [(String, Bool)] 48 | deriving (Eq, Ord, Read, Show, Generic) 49 | 50 | data ChComponentName = ChSetupHsName 51 | | ChLibName 52 | | ChSubLibName String 53 | | ChFLibName String 54 | | ChExeName String 55 | | ChTestName String 56 | | ChBenchName String 57 | deriving (Eq, Ord, Read, Show, Generic) 58 | 59 | newtype ChModuleName = ChModuleName String 60 | deriving (Eq, Ord, Read, Show, Generic) 61 | 62 | data ChEntrypoint = ChSetupEntrypoint -- ^ Almost like 'ChExeEntrypoint' but 63 | -- @main-is@ could either be @"Setup.hs"@ 64 | -- or @"Setup.lhs"@. Since we don't know 65 | -- where the source directory is you have 66 | -- to find these files. 67 | | ChLibEntrypoint { chExposedModules :: [ChModuleName] 68 | , chOtherModules :: [ChModuleName] 69 | , chSignatures :: [ChModuleName] -- backpack only 70 | } 71 | | ChExeEntrypoint { chMainIs :: FilePath 72 | , chOtherModules :: [ChModuleName] 73 | } deriving (Eq, Ord, Read, Show, Generic) 74 | 75 | data ChPkgDb = ChPkgGlobal 76 | | ChPkgUser 77 | | ChPkgSpecific FilePath 78 | deriving (Eq, Ord, Read, Show, Generic) 79 | 80 | data NeedsBuildOutput = ProduceBuildOutput | NoBuildOutput 81 | deriving (Eq, Ord, Read, Show, Generic) 82 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/src/CabalHelper/Shared/Sandbox.hs: -------------------------------------------------------------------------------- 1 | -- cabal-helper: Simple interface to Cabal's configuration state 2 | -- Copyright (C) 2015-2017 Daniel Gröber 3 | -- 4 | -- This program is free software: you can redistribute it and/or modify 5 | -- it under the terms of the GNU General Public License as published by 6 | -- the Free Software Foundation, either version 3 of the License, or 7 | -- (at your option) any later version. 8 | -- 9 | -- This program is distributed in the hope that it will be useful, 10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | -- GNU General Public License for more details. 13 | -- 14 | -- You should have received a copy of the GNU General Public License 15 | -- along with this program. If not, see . 16 | 17 | {-| 18 | Module : CabalHelper.Shared.Sandbox 19 | Description : Extracting information from @cabal.sandbox.config@ files 20 | License : GPL-3 21 | -} 22 | 23 | module CabalHelper.Shared.Sandbox where 24 | 25 | import Control.Applicative 26 | import Data.Char 27 | import Data.Maybe 28 | import Data.List 29 | import Data.Version 30 | import System.FilePath 31 | import System.Directory 32 | import Prelude 33 | 34 | import qualified Data.Traversable as T 35 | 36 | -- | Get the path to the sandbox package-db in a project 37 | getSandboxPkgDb :: FilePath 38 | -- ^ Path to the cabal package root directory (containing the 39 | -- @cabal.sandbox.config@ file) 40 | -> String 41 | -- ^ Cabal build platform, i.e. @buildPlatform@ 42 | -> Version 43 | -- ^ GHC version (@cProjectVersion@ is your friend) 44 | -> IO (Maybe FilePath) 45 | getSandboxPkgDb d platform ghcVer = do 46 | mConf <- T.traverse readFile =<< mightExist (d "cabal.sandbox.config") 47 | return $ fixPkgDbVer <$> (extractSandboxDbDir =<< mConf) 48 | 49 | where 50 | fixPkgDbVer dir = 51 | case takeFileName dir == ghcSandboxPkgDbDir platform ghcVer of 52 | True -> dir 53 | False -> takeDirectory dir ghcSandboxPkgDbDir platform ghcVer 54 | 55 | ghcSandboxPkgDbDir :: String -> Version -> String 56 | ghcSandboxPkgDbDir platform ghcVer = 57 | platform ++ "-ghc-" ++ showVersion ghcVer ++ "-packages.conf.d" 58 | 59 | -- | Extract the sandbox package db directory from the cabal.sandbox.config 60 | -- file. Exception is thrown if the sandbox config file is broken. 61 | extractSandboxDbDir :: String -> Maybe FilePath 62 | extractSandboxDbDir conf = extractValue <$> parse conf 63 | where 64 | key = "package-db:" 65 | keyLen = length key 66 | 67 | parse = listToMaybe . filter (key `isPrefixOf`) . lines 68 | extractValue = CabalHelper.Shared.Sandbox.dropWhileEnd isSpace . dropWhile isSpace . drop keyLen 69 | 70 | 71 | mightExist :: FilePath -> IO (Maybe FilePath) 72 | mightExist f = do 73 | exists <- doesFileExist f 74 | return $ if exists then (Just f) else (Nothing) 75 | 76 | -- dropWhileEnd is not provided prior to base 4.5.0.0. 77 | dropWhileEnd :: (a -> Bool) -> [a] -> [a] 78 | dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) [] 79 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/CompileTest.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ScopedTypeVariables, GADTs #-} 2 | 3 | import System.Environment (getArgs) 4 | import System.Directory 5 | import System.FilePath 6 | import System.Process 7 | import System.Exit 8 | import System.IO 9 | import Control.Exception as E 10 | import Data.List 11 | import Data.Maybe 12 | import Data.Version 13 | import Data.Functor 14 | import Data.Function 15 | import qualified Distribution.Compat.ReadP as Dist 16 | import Distribution.Version (VersionRange, withinRange) 17 | import Distribution.Text 18 | import Control.Arrow 19 | import Control.Monad 20 | import Prelude 21 | 22 | import CabalHelper.Compiletime.Compat.Environment 23 | import CabalHelper.Compiletime.Compat.Version 24 | import CabalHelper.Compiletime.Compile 25 | import CabalHelper.Compiletime.Types 26 | import CabalHelper.Shared.Common 27 | 28 | runReadP'Dist :: Dist.ReadP t t -> String -> t 29 | runReadP'Dist p i = case filter ((=="") . snd) $ Dist.readP_to_S p i of 30 | (a,""):[] -> a 31 | _ -> error $ "Error parsing: " ++ show i 32 | 33 | withinRange'CH :: Version -> VersionRange -> Bool 34 | withinRange'CH v r = 35 | withinRange (fromDataVersion v) r 36 | 37 | setupHOME :: IO () 38 | setupHOME = do 39 | tmp <- fromMaybe "/tmp" <$> lookupEnv "TMPDIR" 40 | let home = tmp "compile-test-home" 41 | _ <- rawSystem "rm" ["-r", home] 42 | createDirectory home 43 | setEnv "HOME" home 44 | 45 | main :: IO () 46 | main = do 47 | args <- getArgs 48 | case args of 49 | "list-versions":[] -> do 50 | mapM_ print =<< (allCabalVersions <$> ghcVersion defaultOptions) 51 | "list-versions":ghc_ver_str:[] -> 52 | mapM_ print $ allCabalVersions (parseVer ghc_ver_str) 53 | _ -> 54 | test args 55 | 56 | test args = do 57 | let action 58 | | null args = testAllCabalVersions 59 | | otherwise = testCabalVersions $ map parseVer' args 60 | 61 | setupHOME 62 | 63 | _ <- rawSystem "cabal" ["update"] 64 | 65 | action 66 | 67 | parseVer' :: String -> Either HEAD Version 68 | parseVer' "HEAD" = Left HEAD 69 | parseVer' v = Right $ parseVer v 70 | 71 | allCabalVersions :: Version -> [Version] 72 | allCabalVersions ghc_ver = let 73 | cabal_versions :: [Version] 74 | cabal_versions = map parseVer 75 | -- "1.14.0" -- not supported at runtime 76 | [ "1.16.0" 77 | , "1.16.0.1" 78 | , "1.16.0.2" 79 | , "1.16.0.3" 80 | , "1.18.0" 81 | , "1.18.1" 82 | , "1.18.1.1" 83 | , "1.18.1.2" 84 | , "1.18.1.3" 85 | , "1.18.1.4" 86 | , "1.18.1.5" 87 | , "1.18.1.6" 88 | , "1.18.1.7" 89 | , "1.20.0.0" 90 | , "1.20.0.1" 91 | , "1.20.0.2" 92 | , "1.20.0.3" 93 | , "1.20.0.4" 94 | , "1.22.0.0" 95 | , "1.22.1.0" 96 | , "1.22.1.1" 97 | , "1.22.2.0" 98 | , "1.22.3.0" 99 | , "1.22.4.0" 100 | , "1.22.5.0" 101 | , "1.22.6.0" 102 | , "1.22.7.0" 103 | , "1.22.8.0" 104 | , "1.24.0.0" 105 | , "1.24.1.0" 106 | , "1.24.2.0" 107 | , "2.0.0.2" 108 | , "2.0.1.0" 109 | , "2.0.1.1" 110 | , "2.2.0.0" 111 | , "2.2.0.1" 112 | ] 113 | 114 | constraint :: VersionRange 115 | constraint = 116 | fromMaybe (snd $ last constraint_table) $ 117 | fmap snd $ 118 | find (and . (zipWith (==) `on` versionBranch) ghc_ver . fst) $ 119 | constraint_table 120 | 121 | constraint_table = 122 | map (parseVer *** runReadP'Dist parse) $ 123 | [ ("7.4" , ">= 1.14 && < 2") 124 | , ("7.6" , ">= 1.16 && < 2") 125 | , ("7.8" , ">= 1.18 && < 2") 126 | , ("7.10" , ">= 1.22.2 && < 2") 127 | , ("8.0.1", ">= 1.24 ") 128 | , ("8.0.2", ">= 1.24.2 ") 129 | , ("8.2.1", ">= 2.0.0.2 ") 130 | , ("8.2.2", ">= 2.0.0.2 ") 131 | , ("8.4.1", ">= 2.0.0.2 ") 132 | , ("8.4.2", ">= 2.2.0.1 ") 133 | ] 134 | in 135 | reverse $ filter (flip withinRange'CH constraint) cabal_versions 136 | 137 | 138 | testAllCabalVersions :: IO () 139 | testAllCabalVersions = do 140 | ghc_ver <- ghcVersion defaultOptions 141 | let relevant_cabal_versions = allCabalVersions ghc_ver 142 | testCabalVersions $ map Right relevant_cabal_versions ++ [Left HEAD] 143 | 144 | testCabalVersions :: [Either HEAD Version] -> IO () 145 | testCabalVersions versions = do 146 | rvs <- forM versions $ \ver -> do 147 | let sver = either show showVersion ver 148 | hPutStrLn stderr $ "\n\n\n\n\n\n====== Compiling with Cabal-" ++ sver 149 | compilePrivatePkgDb ver 150 | 151 | let printStatus (cv, rv) = putStrLn $ "- Cabal "++ver++" "++status 152 | where ver = case cv of Left _ -> "HEAD"; Right v -> showVersion v 153 | status = case rv of 154 | Right _ -> 155 | "succeeded" 156 | Left rvc -> 157 | "failed (exit code "++show rvc++")" 158 | 159 | let drvs = versions `zip` rvs 160 | 161 | mapM_ printStatus drvs 162 | if any isLeft' $ map snd $ filter ((/=Left HEAD) . fst) drvs 163 | then exitFailure 164 | else exitSuccess 165 | 166 | where 167 | isLeft' (Left _) = True 168 | isLeft' (Right _) = False 169 | 170 | compilePrivatePkgDb :: Either HEAD Version -> IO (Either ExitCode FilePath) 171 | compilePrivatePkgDb eCabalVer = do 172 | res <- E.try $ installCabal defaultOptions { oVerbose = True } eCabalVer 173 | case res of 174 | Right (db, cabalVer) -> 175 | compileWithPkg db cabalVer 176 | Left (ioe :: IOException) -> do 177 | print ioe 178 | return $ Left (ExitFailure 1) 179 | 180 | compileWithPkg :: PackageDbDir 181 | -> CabalVersion 182 | -> IO (Either ExitCode FilePath) 183 | compileWithPkg db cabalVer = do 184 | appdir <- appCacheDir 185 | let comp = 186 | CompileWithCabalPackage (Just db) cabalVer [cabalPkgId cabalVer] CPSGlobal 187 | compile 188 | comp 189 | (compPaths appdir (error "compile-test: distdir not available") comp) 190 | defaultOptions { oVerbose = True } 191 | 192 | 193 | cabalPkgId :: CabalVersion -> String 194 | cabalPkgId (CabalHEAD _commitid) = "Cabal" 195 | cabalPkgId (CabalVersion v) = "Cabal-" ++ showVersion v 196 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/GhcSession.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TupleSections, ScopedTypeVariables #-} 2 | module Main where 3 | 4 | import GHC 5 | import GHC.Paths (libdir) 6 | import DynFlags 7 | 8 | import qualified Control.Exception as E 9 | import Control.Monad 10 | import Control.Monad.IO.Class 11 | import Data.List 12 | import Data.Version 13 | import System.Environment (getArgs) 14 | import System.Exit 15 | import System.FilePath (()) 16 | import System.Directory 17 | import System.IO 18 | import System.IO.Temp 19 | import System.Process (rawSystem, readProcess) 20 | 21 | import Distribution.Helper 22 | 23 | import CabalHelper.Shared.Common 24 | 25 | 26 | main :: IO () 27 | main = do 28 | args <- getArgs 29 | topdir <- getCurrentDirectory 30 | res <- mapM (setup topdir test) $ case args of 31 | [] -> [ ("tests/exelib" , parseVer "1.10", parseVer "0") 32 | , ("tests/exeintlib", parseVer "2.0", parseVer "0") 33 | , ("tests/fliblib" , parseVer "2.0", parseVer "0") 34 | , ("tests/bkpregex" , parseVer "2.0", parseVer "8.1") 35 | -- min Cabal lib ver -^ min GHC ver -^ 36 | ] 37 | xs -> map (, parseVer "0", parseVer "0") xs 38 | 39 | if any (==False) $ concat res 40 | then exitFailure 41 | else exitSuccess 42 | 43 | cabalInstallVersion :: IO Version 44 | cabalInstallVersion = 45 | parseVer . trim <$> readProcess "cabal" ["--numeric-version"] "" 46 | 47 | ghcVersion :: IO Version 48 | ghcVersion = 49 | parseVer . trim <$> readProcess "ghc" ["--numeric-version"] "" 50 | 51 | cabalInstallBuiltinCabalVersion :: IO Version 52 | cabalInstallBuiltinCabalVersion = 53 | parseVer . trim <$> readProcess "cabal" 54 | ["act-as-setup", "--", "--numeric-version"] "" 55 | 56 | setup :: FilePath -> (FilePath -> IO [Bool]) -> (FilePath, Version, Version) -> IO [Bool] 57 | setup topdir act (srcdir, min_cabal_ver, min_ghc_ver) = do 58 | ci_ver <- cabalInstallVersion 59 | c_ver <- cabalInstallBuiltinCabalVersion 60 | g_ver <- ghcVersion 61 | let mreason 62 | | (ci_ver < parseVer "1.24") = 63 | Just $ "cabal-install-" ++ showVersion ci_ver ++ " is too old" 64 | | c_ver < min_cabal_ver = 65 | Just $ "Cabal-" ++ showVersion c_ver 66 | ++ " < " ++ showVersion min_cabal_ver 67 | | g_ver < min_ghc_ver = 68 | Just $ "ghc-" ++ showVersion g_ver 69 | ++ " < " ++ showVersion min_ghc_ver 70 | | otherwise = 71 | Nothing 72 | 73 | case mreason of 74 | Just reason -> do 75 | putStrLn $ "Skipping test '" ++ srcdir ++ "' because " ++ reason ++ "." 76 | return [] 77 | Nothing -> do 78 | putStrLn $ "Running test '" ++ srcdir ++ "' ------------------------------" 79 | withSystemTempDirectory "cabal-helper.ghc-session.test" $ \dir -> do 80 | setCurrentDirectory $ topdir srcdir 81 | run "cabal" [ "sdist", "--output-dir", dir ] 82 | 83 | setCurrentDirectory dir 84 | run "cabal" [ "configure" ] 85 | 86 | act dir 87 | 88 | run :: String -> [String] -> IO () 89 | run x xs = do 90 | print $ x:xs 91 | ExitSuccess <- rawSystem x xs 92 | return () 93 | 94 | test :: FilePath -> IO [Bool] 95 | test dir = do 96 | let qe = mkQueryEnv dir (dir "dist") 97 | cs <- runQuery qe $ components $ (,,,) <$> entrypoints <.> ghcOptions <.> needsBuildOutput 98 | forM cs $ \(ep, opts, nb, cn) -> do 99 | 100 | putStrLn $ "\n" ++ show cn ++ ":::: " ++ show nb 101 | 102 | when (nb == ProduceBuildOutput) $ do 103 | run "cabal" [ "build" ] 104 | 105 | let opts' = "-Werror" : opts 106 | 107 | let sopts = intercalate " " $ map formatArg $ "\nghc" : opts' 108 | putStrLn $ "\n" ++ show cn ++ ": " ++ sopts 109 | hFlush stdout 110 | compileModule nb ep opts' 111 | where 112 | formatArg x 113 | | "-" `isPrefixOf` x = "\n "++x 114 | | otherwise = x 115 | 116 | 117 | compileModule :: NeedsBuildOutput -> ChEntrypoint -> [String] -> IO Bool 118 | compileModule nb ep opts = do 119 | 120 | putStrLn $ "compiling:" ++ show ep ++ " (" ++ show nb ++ ")" 121 | 122 | E.handle (\(ec :: ExitCode) -> print ec >> return False) $ do 123 | 124 | defaultErrorHandler defaultFatalMessager defaultFlushOut $ do 125 | 126 | runGhc (Just libdir) $ do 127 | 128 | handleSourceError (\e -> GHC.printException e >> return False) $ do 129 | 130 | let target = case nb of 131 | ProduceBuildOutput -> HscNothing -- AZ: what should this be? 132 | NoBuildOutput -> HscInterpreted 133 | 134 | dflags0 <- getSessionDynFlags 135 | let dflags1 = dflags0 { 136 | ghcMode = CompManager 137 | , ghcLink = LinkInMemory 138 | , hscTarget = target 139 | , optLevel = 0 140 | } 141 | 142 | (dflags2, _, _) <- parseDynamicFlags dflags1 (map noLoc opts) 143 | _ <- setSessionDynFlags dflags2 144 | 145 | ts <- mapM (\t -> guessTarget t Nothing) $ 146 | case ep of 147 | ChLibEntrypoint ms ms' ss -> map unChModuleName $ ms ++ ms' ++ ss 148 | ChExeEntrypoint m' ms -> 149 | let 150 | 151 | -- The options first clear out includes, then put in the build dir. We want the 152 | -- first one after that, so "regex-example" in the following case 153 | -- 154 | -- ,"-i" 155 | -- ,"-idist/build/regex-example" 156 | -- ,"-iregex-example" 157 | firstInclude = drop 2 $ head $ drop 2 $ filter (isPrefixOf "-i") opts 158 | m = firstInclude m' 159 | in [m] ++ map unChModuleName ms 160 | ChSetupEntrypoint -> ["Setup.hs"] 161 | 162 | let ts' = case nb of 163 | NoBuildOutput -> map (\t -> t { targetAllowObjCode = False }) ts 164 | ProduceBuildOutput -> ts 165 | 166 | setTargets ts' 167 | _ <- load LoadAllTargets 168 | 169 | when (nb == NoBuildOutput) $ do 170 | setContext $ case ep of 171 | ChLibEntrypoint ms ms' ss -> 172 | map (IIModule . mkModuleName . unChModuleName) $ ms ++ ms' ++ ss 173 | ChExeEntrypoint _ ms -> 174 | map (IIModule . mkModuleName . unChModuleName) $ ChModuleName "Main" : ms 175 | ChSetupEntrypoint -> 176 | map (IIModule . mkModuleName) ["Main"] 177 | 178 | liftIO $ print ExitSuccess 179 | return True 180 | 181 | unChModuleName :: ChModuleName -> String 182 | unChModuleName (ChModuleName mn) = mn 183 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/bkpregex.cabal: -------------------------------------------------------------------------------- 1 | name: bkpregex 2 | version: 0.1.0.0 3 | build-type: Simple 4 | cabal-version: 2.0 5 | 6 | library str-impls 7 | build-depends: base, bytestring 8 | exposed-modules: Str.String, Str.ByteString 9 | hs-source-dirs: str-impls 10 | 11 | library regex-types 12 | build-depends: base 13 | exposed-modules: Regex.Types 14 | hs-source-dirs: regex-types 15 | 16 | library regex-indef 17 | build-depends: base, regex-types 18 | signatures: Str 19 | exposed-modules: Regex 20 | hs-source-dirs: regex-indef 21 | 22 | executable regex-example 23 | main-is: Main.hs 24 | build-depends: base, regex-indef, regex-types, str-impls 25 | mixins: regex-indef (Regex as Regex.String) 26 | requires (Str as Str.String), 27 | regex-indef (Regex as Regex.ByteString) 28 | requires (Str as Str.ByteString) 29 | hs-source-dirs: regex-example 30 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/regex-example/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Main where 3 | 4 | import Regex.Types 5 | import qualified Regex.String 6 | import qualified Regex.ByteString 7 | 8 | nocs = Rep (Alt (Sym 'a') (Sym 'b')) 9 | onec = Seq nocs (Sym 'c') 10 | evencs = Seq (Rep (Seq onec onec)) nocs 11 | main = print (Regex.String.accept evencs "acc") >> 12 | print (Regex.ByteString.accept evencs "acc") 13 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/regex-indef/Regex.hs: -------------------------------------------------------------------------------- 1 | module Regex where 2 | 3 | import Prelude hiding (null) 4 | import Str 5 | import Regex.Types 6 | 7 | accept :: Reg -> Str -> Bool 8 | accept Eps u = null u 9 | accept (Sym c) u = u == singleton c 10 | accept (Alt p q) u = accept p u || accept q u 11 | accept (Seq p q) u = 12 | or [accept p u1 && accept q u2 | (u1, u2) <- splits u] 13 | accept (Rep r) u = 14 | or [and [accept r ui | ui <- ps] | ps <- parts u] 15 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/regex-indef/Str.hsig: -------------------------------------------------------------------------------- 1 | signature Str where 2 | 3 | data Str 4 | instance Eq Str 5 | 6 | null :: Str -> Bool 7 | singleton :: Char -> Str 8 | splits :: Str -> [(Str, Str)] 9 | parts :: Str -> [[Str]] 10 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/regex-types/Regex/Types.hs: -------------------------------------------------------------------------------- 1 | module Regex.Types where 2 | 3 | data Reg = Eps 4 | | Sym Char 5 | | Alt Reg Reg 6 | | Seq Reg Reg 7 | | Rep Reg 8 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/str-impls/Str/ByteString.hs: -------------------------------------------------------------------------------- 1 | module Str.ByteString(module Data.ByteString.Char8, module Str.ByteString) where 2 | 3 | import Prelude hiding (length, null, splitAt) 4 | import Data.ByteString.Char8 5 | import Data.ByteString 6 | 7 | type Str = ByteString 8 | 9 | splits :: Str -> [(Str, Str)] 10 | splits s = fmap (\n -> splitAt n s) [0..length s] 11 | 12 | parts :: Str -> [[Str]] 13 | parts s | null s = [[]] 14 | | otherwise = do 15 | n <- [1..length s] 16 | let (l, r) = splitAt n s 17 | fmap (l:) (parts r) 18 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/bkpregex/str-impls/Str/String.hs: -------------------------------------------------------------------------------- 1 | module Str.String where 2 | 3 | import Prelude hiding (null) 4 | import qualified Prelude as P 5 | 6 | type Str = String 7 | 8 | null :: Str -> Bool 9 | null = P.null 10 | 11 | singleton :: Char -> Str 12 | singleton c = [c] 13 | 14 | splits :: Str -> [(Str, Str)] 15 | splits [] = [([], [])] 16 | splits (c:cs) = ([], c:cs):[(c:s1,s2) | (s1,s2) <- splits cs] 17 | 18 | parts :: Str -> [[Str]] 19 | parts [] = [[]] 20 | parts [c] = [[[c]]] 21 | parts (c:cs) = concat [[(c:p):ps, [c]:p:ps] | p:ps <- parts cs] 22 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exeintlib/Exe.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Lib 4 | 5 | main = print lib 6 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exeintlib/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exeintlib/exeintlib.cabal: -------------------------------------------------------------------------------- 1 | name: exeintlib 2 | version: 0 3 | build-type: Simple 4 | cabal-version: >=2.0 5 | 6 | library 7 | exposed-modules: Lib 8 | hs-source-dirs: lib 9 | build-depends: base, filepath, intlib 10 | default-language: Haskell2010 11 | 12 | library intlib 13 | exposed-modules: IntLib 14 | hs-source-dirs: intlib 15 | build-depends: base, directory 16 | default-language: Haskell2010 17 | 18 | executable exe 19 | main-is: Exe.hs 20 | build-depends: base, exeintlib 21 | default-language: Haskell2010 22 | 23 | test-suite exe-test 24 | type: exitcode-stdio-1.0 25 | main-is: Exe.hs 26 | build-depends: base, exeintlib 27 | 28 | benchmark exe-bench 29 | type: exitcode-stdio-1.0 30 | main-is: Exe.hs 31 | build-depends: base, exeintlib 32 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exeintlib/intlib/IntLib.hs: -------------------------------------------------------------------------------- 1 | module IntLib where 2 | 3 | import System.Directory 4 | 5 | directory = doesFileExist "Exe.hs" 6 | 7 | intlib = 1 8 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exeintlib/lib/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib where 2 | 3 | import System.FilePath 4 | import IntLib 5 | 6 | filepath = "a" "b" 7 | 8 | lib = 1 + intlib 9 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exelib/Exe.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Lib 4 | 5 | main = print foo 6 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exelib/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exelib/exelib.cabal: -------------------------------------------------------------------------------- 1 | name: exelib 2 | version: 0 3 | build-type: Simple 4 | cabal-version: >=1.10 5 | 6 | library 7 | exposed-modules: Lib 8 | hs-source-dirs: lib 9 | build-depends: base, filepath, directory 10 | default-language: Haskell2010 11 | 12 | executable exelib 13 | main-is: Exe.hs 14 | build-depends: base, exelib 15 | default-language: Haskell2010 16 | 17 | test-suite exe-test 18 | type: exitcode-stdio-1.0 19 | main-is: Exe.hs 20 | build-depends: base, exelib 21 | 22 | benchmark exe-bench 23 | type: exitcode-stdio-1.0 24 | main-is: Exe.hs 25 | build-depends: base, exelib 26 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/exelib/lib/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib where 2 | 3 | import System.Directory 4 | import System.FilePath 5 | 6 | filepath = "a" "b" 7 | directory = doesFileExist "Exe.hs" 8 | foo = 1 9 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/fliblib/FLib.hs: -------------------------------------------------------------------------------- 1 | module FLib where 2 | 3 | import Lib 4 | 5 | flib = print foo 6 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/fliblib/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/fliblib/fliblib.cabal: -------------------------------------------------------------------------------- 1 | name: fliblib 2 | version: 0 3 | build-type: Simple 4 | cabal-version: >=1.10 5 | 6 | library 7 | exposed-modules: Lib 8 | hs-source-dirs: lib 9 | build-depends: base, filepath, directory 10 | default-language: Haskell2010 11 | 12 | foreign-library flib 13 | other-modules: FLib 14 | build-depends: base, fliblib 15 | hs-source-dirs: . 16 | type: native-shared 17 | if os(Windows) 18 | options: standalone 19 | default-language: Haskell2010 -------------------------------------------------------------------------------- /vendor/cabal-helper-0.8.1.2/tests/fliblib/lib/Lib.hs: -------------------------------------------------------------------------------- 1 | module Lib where 2 | 3 | import System.Directory 4 | import System.FilePath 5 | 6 | filepath = "a" "b" 7 | directory = doesFileExist "Exe.hs" 8 | foo = 1 9 | --------------------------------------------------------------------------------