├── .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 = "";
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 | You need to enable JavaScript to run this app.
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 |
20 |
21 |
22 | {{outlet}}
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/javascript/app/templates/bad-url.hbs:
--------------------------------------------------------------------------------
1 |
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 |
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 |
18 |
21 |
--------------------------------------------------------------------------------
/javascript/app/templates/components/haskell-module.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if declarations}}
3 |
4 |
9 | {{#if showDeclarations}}
10 |
11 |
12 | {{#infinite-list containerElementId="declarations-content" elements=filteredDeclarations perPage=30 as |declaration|}}
13 |
14 |
18 |
19 | {{/infinite-list}}
20 |
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 |
3 | {{#unless isBinder}}
4 | {{#if location}}
5 |
6 |
7 | {{/if}}
8 | {{/unless}}
9 | {{#if isExternalIdentifier}}
10 |
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 |
9 |
10 | {{yield "body"}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/javascript/app/templates/components/input-with-autocomplete.hbs:
--------------------------------------------------------------------------------
1 |
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 |
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 | {{expandTypeSynonymsLabel}}
7 |
8 |
9 | {{/if}}
10 | {{/unless}}
11 |
--------------------------------------------------------------------------------
/javascript/app/templates/package.hbs:
--------------------------------------------------------------------------------
1 |
2 |
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 |
65 | {{/each}}
66 |
67 | {{/if}}
68 |
69 |
70 | {{#paginated-list url=referencesUrl foundWhere=(concat "in
" packageId " ") as |files|}}
71 |
72 | {{#each files as |file|}}
73 |
74 |
75 | {{#each file.references as |reference|}}
76 | {{{reference.sourceCodeHtml}}}
77 | {{/each}}
78 |
79 | {{/each}}
80 |
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 |
6 | {{#each modulesFiltered as |module|}}
7 |
8 | {{module}}
9 |
10 | {{/each}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/javascript/app/templates/package/search.hbs:
--------------------------------------------------------------------------------
1 |
4 |
5 | {{#paginated-list url=model.url as |identifiers|}}
6 |
7 | {{#each identifiers as |identifier|}}
8 |
9 | {{identifier.demangledOccName}} :: {{type-signature-text components=identifier.idType.components}}
10 |
11 |
16 | {{{identifier.doc}}}
17 |
18 | {{/each}}
19 |
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 |
23 |
24 |
25 | {{input class="form-control" type="text" value=query placeholder="Package name"}}
26 | Number of packages : {{packages.length}}
27 |
28 |
29 |
30 |
31 | {{#infinite-list containerElementId="packages" elements=packages perPage=80 as |package|}}
32 |
33 | {{#each package.versions as |version index|}}
34 | {{#link-to 'package' (concat package.name "-" version)}}
35 | {{#if (gt index 0)}}
36 | {{version}}
37 | {{else}}
38 | {{package.name}}-{{version}}
39 | {{/if}}
40 | {{/link-to}}
41 | {{/each}}
42 |
43 | {{/infinite-list}}
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/javascript/app/templates/search.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 | [](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 |
--------------------------------------------------------------------------------