1{-# LANGUAGE OverloadedStrings #-}
9import Control.Applicative ((<|>))
10import Control.Monad (guard)
11import Control.Monad.Trans (MonadIO(liftIO))
12import qualified Data.ByteString.Builder as B
13import qualified Data.ByteString as BS
14import qualified Data.HashMap.Strict as HashMap
15import Data.Monoid ((<>))
16import qualified Data.NonEmptyList as NE
17import qualified System.Directory as Dir
18import System.FilePath as FP
19import Snap.Core hiding (path)
20import Snap.Http.Server
21import Snap.Util.FileServe
23import qualified BackgroundWriter as BW
25import qualified Elm.Details as Details
26import qualified Develop.Generate.Help as Help
27import qualified Develop.Generate.Index as Index
28import qualified Develop.StaticFiles as StaticFiles
29import qualified Generate.Html as Html
30import qualified Generate
31import qualified Reporting
32import qualified Reporting.Exit as Exit
33import qualified Reporting.Task as Task
47run :: () -> Flags -> IO ()
48run () (Flags maybePort) =
49 do let port = maybe 8000 id maybePort
50 putStrLn $ "Go to http://localhost:" ++ show port ++ " to see your project dashboard."
51 httpServe (config port) $
53 <|> serveDirectoryWith directoryConfig "."
58config :: Int -> Config Snap a
60 setVerbose False $ setPort port $
61 setAccessLog ConfigNoLog $ setErrorLog ConfigNoLog $ defaultConfig
68directoryConfig :: MonadSnap m => DirectoryConfig m
72 , indexGenerator = \pwd ->
73 do modifyResponse $ setContentType "text/html;charset=utf-8"
74 writeBuilder =<< liftIO (Index.generate pwd)
84 do modifyResponse $ setResponseStatus 404 "Not Found"
85 modifyResponse $ setContentType "text/html;charset=utf-8"
86 writeBuilder $ Help.makePageHtml "NotFound" Nothing
95 do path <- getSafePath
96 guard =<< liftIO (Dir.doesFileExist path)
97 serveElm path <|> serveFilePretty path
101-- SERVE FILES + CODE HIGHLIGHTING
104serveFilePretty :: FilePath -> Snap ()
105serveFilePretty path =
108 getSubExts (takeExtensions path)
110 case mconcat (map lookupMimeType possibleExtensions) of
115 serveFileAs mimeType path
118getSubExts :: String -> [String]
119getSubExts fullExtension =
120 if null fullExtension then
123 fullExtension : getSubExts (takeExtensions (drop 1 fullExtension))
126serveCode :: String -> Snap ()
128 do code <- liftIO (BS.readFile path)
129 modifyResponse (setContentType "text/html")
131 Help.makeCodeHtml ('~' : '/' : path) (B.byteString code)
138serveElm :: FilePath -> Snap ()
140 do guard (takeExtension path == ".elm")
141 modifyResponse (setContentType "text/html")
142 result <- liftIO $ compile path
148 writeBuilder $ Help.makePageHtml "Errors" $ Just $
149 Exit.toJson $ Exit.reactorToReport exit
152compile :: FilePath -> IO (Either Exit.Reactor B.Builder)
154 do maybeRoot <- Stuff.findRoot
157 return $ Left $ Exit.ReactorNoOutline
160 BW.withScope $ \scope -> Stuff.withRootLock root $ Task.run $
161 do details <- Task.eio Exit.ReactorBadDetails $ Details.load Reporting.silent scope root
162 artifacts <- Task.eio Exit.ReactorBadBuild $ Build.fromPaths Reporting.silent root details (NE.List path [])
163 javascript <- Task.mapError Exit.ReactorBadGenerate $ Generate.dev root details artifacts
164 let (NE.List name _) = Build.getRootNames artifacts
165 return $ Html.sandwich name javascript
169-- SERVE STATIC ASSETS
172serveAssets :: Snap ()
174 do path <- getSafePath
175 case StaticFiles.lookup path of
179 Just (content, mimeType) ->
180 do modifyResponse (setContentType (mimeType <> ";charset=utf-8"))
188lookupMimeType :: FilePath -> Maybe BS.ByteString
190 HashMap.lookup ext mimeTypeDict
193(==>) :: a -> b -> (a,b)
198mimeTypeDict :: HashMap.HashMap FilePath BS.ByteString
201 [ ".asc" ==> "text/plain"
202 , ".asf" ==> "video/x-ms-asf"
203 , ".asx" ==> "video/x-ms-asf"
204 , ".avi" ==> "video/x-msvideo"
205 , ".bz2" ==> "application/x-bzip"
206 , ".css" ==> "text/css"
207 , ".dtd" ==> "text/xml"
208 , ".dvi" ==> "application/x-dvi"
209 , ".gif" ==> "image/gif"
210 , ".gz" ==> "application/x-gzip"
211 , ".htm" ==> "text/html"
212 , ".html" ==> "text/html"
213 , ".ico" ==> "image/x-icon"
214 , ".jpeg" ==> "image/jpeg"
215 , ".jpg" ==> "image/jpeg"
216 , ".js" ==> "text/javascript"
217 , ".json" ==> "application/json"
218 , ".m3u" ==> "audio/x-mpegurl"
219 , ".mov" ==> "video/quicktime"
220 , ".mp3" ==> "audio/mpeg"
221 , ".mp4" ==> "video/mp4"
222 , ".mpeg" ==> "video/mpeg"
223 , ".mpg" ==> "video/mpeg"
224 , ".ogg" ==> "application/ogg"
225 , ".otf" ==> "font/otf"
226 , ".pac" ==> "application/x-ns-proxy-autoconfig"
227 , ".pdf" ==> "application/pdf"
228 , ".png" ==> "image/png"
229 , ".qt" ==> "video/quicktime"
230 , ".sfnt" ==> "font/sfnt"
231 , ".sig" ==> "application/pgp-signature"
232 , ".spl" ==> "application/futuresplash"
233 , ".svg" ==> "image/svg+xml"
234 , ".swf" ==> "application/x-shockwave-flash"
235 , ".tar" ==> "application/x-tar"
236 , ".tar.bz2" ==> "application/x-bzip-compressed-tar"
237 , ".tar.gz" ==> "application/x-tgz"
238 , ".tbz" ==> "application/x-bzip-compressed-tar"
239 , ".text" ==> "text/plain"
240 , ".tgz" ==> "application/x-tgz"
241 , ".ttf" ==> "font/ttf"
242 , ".txt" ==> "text/plain"
243 , ".wav" ==> "audio/x-wav"
244 , ".wax" ==> "audio/x-ms-wax"
245 , ".webm" ==> "video/webm"
246 , ".webp" ==> "image/webp"
247 , ".wma" ==> "audio/x-ms-wma"
248 , ".wmv" ==> "video/x-ms-wmv"
249 , ".woff" ==> "font/woff"
250 , ".woff2" ==> "font/woff2"
251 , ".xbm" ==> "image/x-xbitmap"
252 , ".xml" ==> "text/xml"
253 , ".xpm" ==> "image/x-xpixmap"
254 , ".xwd" ==> "image/x-xwindowdump"
255 , ".zip" ==> "application/zip"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list