├── .gitignore ├── .travis.yml ├── LICENSE ├── README.org ├── agda-snippets-hakyll ├── LICENSE ├── Setup.hs ├── agda-snippets-hakyll.cabal └── src │ └── Hakyll │ └── Contrib │ └── Agda.hs ├── agda-snippets ├── LICENSE ├── Setup.hs ├── agda-snippets.cabal ├── app │ └── Main.hs └── src │ └── Agda │ └── Contrib │ └── Snippets.hs ├── stack-travis.yaml └── stack.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use new container infrastructure to enable caching 2 | sudo: false 3 | 4 | # Choose a lightweight base image; we provide our own build tools. 5 | language: c 6 | 7 | # GHC depends on GMP. You can add other dependencies here as well. 8 | addons: 9 | apt: 10 | packages: 11 | - libgmp-dev 12 | 13 | # The different configurations we want to test. You could also do things like 14 | # change flags or use --stack-yaml to point to a different file. 15 | env: 16 | - ARGS="--stack-yaml stack-travis.yaml" 17 | 18 | before_install: 19 | # Download and unpack the stack executable 20 | - mkdir -p ~/.local/bin 21 | - export PATH=$HOME/.local/bin:$PATH 22 | - travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' 23 | 24 | # This line does all of the work: installs GHC if necessary, build the library, 25 | # executables, and test suites, and runs the test suites. --no-terminal works 26 | # around some quirks in Travis's terminal implementation. 27 | script: 28 | - stack $ARGS --no-terminal --install-ghc install alex happy 29 | - travis_wait stack $ARGS --no-terminal --install-ghc test --haddock 30 | 31 | # Caching so the next build will be fast too. 32 | cache: 33 | directories: 34 | - $HOME/.stack 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Liam O'Connor (c) 2015 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Liam O'Connor nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | [[https://travis-ci.org/liamoc/agda-snippets][file:https://travis-ci.org/liamoc/agda-snippets.svg]] [[http://haskell.org][file:https://img.shields.io/badge/language-Haskell-blue.svg]] [[https://github.com/liamoc/agda-snippets/blob/master/LICENSE][file:http://img.shields.io/badge/license-BSD3-brightgreen.svg]] 2 | 3 | * Agda Snippets 4 | 5 | These libraries allow you to use Literate Agda for formats not natively supported 6 | by the Agda compiler, such as Pandoc Markdown. It's made of two components, ~agda-snippets~, 7 | which provides the main functionality for arbitrary text documents, and ~agda-snippets-hakyll~, 8 | which provides convenience integration for Hakyll pages. 9 | 10 | ** ~agda-snippets~ 11 | 12 | [[http://hackage.haskell.org/package/agda-snippets][file:https://img.shields.io/hackage/v/agda-snippets.svg]] 13 | 14 | Provides a very simple function that translates just the code blocks 15 | of a literate Agda file to colourised, hyperlinked HTML. The output of this 16 | can then be run through Pandoc or other document processors to allow literate 17 | Agda to be comfortably written in any format that allows inline HTML snippets. 18 | 19 | There is also a simple command-line application (~agda-snippets~) included 20 | that can be used as a standalone file processor. 21 | 22 | The location of library source hyperlinks is configurable, as is the CSS class 23 | given to Agda code blocks. 24 | 25 | Usually, I try to keep the development version of this library working with the 26 | development version of Agda. This is not always 100% reliable, as I only update 27 | the library when I update my Agda installation. It will always work with the latest 28 | stable version of Agda. 29 | 30 | Stable releases of this library are published on Hackage, as per usual, and these 31 | releases are fixed to particular Agda versions. The version of the library matches 32 | the exact Agda version it corresponds to, and thus it doesn't follow the PVP. 33 | 34 | To see what the output looks like, you can look at [[http://liamoc.net/posts/2015-08-23-verified-compiler.html][this article]] on my blog, which 35 | makes use of this library to render the Agda snippets. 36 | 37 | ** ~agda-snippets-hakyll~ 38 | 39 | [[http://hackage.haskell.org/package/agda-snippets-hakyll][file:https://img.shields.io/hackage/v/agda-snippets-hakyll.svg]] 40 | 41 | This library provides various compilers for ~lagda~ documents, that uses Pandoc 42 | to compile the text in the literate Agda document, and ~agda-snippets~ to render the HTML 43 | in the blocks. It should mostly be a drop-in replacement for compilers such as the basic 44 | Pandoc compiler. 45 | 46 | ** Building/Installing 47 | 48 | You can install these libraries and the document processing tool from Hackage 49 | using ~stack~: 50 | 51 | #+BEGIN_SRC sh 52 | stack install agda-snippets # resp agda-snippets-hakyll 53 | #+END_SRC 54 | 55 | or using Cabal: 56 | 57 | #+BEGIN_SRC sh 58 | cabal update 59 | cabal install agda-snippets # resp agda-snippets-hakyll 60 | #+END_SRC 61 | 62 | If you want to use the development version from this repository, 63 | you will have to have the correct version of Agda already available. The simplest 64 | way to do this is to edit ~stack.yaml~, to point to the location of your Agda 65 | repository, and use ~stack~ to build the library. 66 | 67 | ** Using 68 | 69 | *** ~agda-snippets~ 70 | 71 | **** The executable 72 | 73 | The executable ~agda-snippets~, once installed, can be invoked according to the following schema: 74 | 75 | #+BEGIN_SRC 76 | agda-snippets input-file.lagda output-file css-class-name /lib/uri/ [agda options] 77 | #+END_SRC 78 | 79 | If you're using ~stack~, you may wish to invoke a local copy using ~stack exec~: 80 | 81 | #+BEGIN_SRC 82 | stack exec agda-snippets -- input-file.lagda output-file css-class-name /lib/uri/ [agda options] 83 | #+END_SRC 84 | 85 | The arguments are as follows: 86 | 87 | - ~input-file.lagda~ - The Literate Agda file to process. 88 | - ~output-file~ - Where to write the output text, where code blocks are replaced with HTML. 89 | - ~css-class-name~ - The name of the CSS class to assign to Agda code blocks. 90 | - ~/lib/uri/~ - The base URI where Agda library listings are located. This is for hyperlinks for sources imported from (e.g) the Agda standard library. 91 | - ~[agda options]~ - Any additional options are passed directly into Agda. 92 | 93 | **** As a library 94 | 95 | The library interface consists of a single function, ~renderAgdaSnippets~, which has an interface more or less exactly like the executable above. 96 | 97 | See the [[http://hackage.haskell.org/package/agda-snippets][Haddocks]] in the Hackage listing for more details. 98 | 99 | *** ~agda-snippets-hakyll~ 100 | 101 | A basic example is as follows: 102 | 103 | #+BEGIN_SRC haskell 104 | main = do 105 | hakyll $ 106 | match "posts/*.lagda" $ do 107 | route $ setExtension "html" 108 | compile $ literateAgdaCompiler defaultOptions Markdown nullURI 109 | #+END_SRC 110 | 111 | A variety of other compilers exist to add pandoc options or arbitrary document transformations. See the haddocks for details. 112 | 113 | ** Credits 114 | 115 | Some parts of this code were based on a few snippets written by Daniel Peebles a long time ago. Not sure how much of it is still his code, but thanks are due to him. 116 | -------------------------------------------------------------------------------- /agda-snippets-hakyll/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Liam O'Connor 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Liam O'Connor nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /agda-snippets-hakyll/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /agda-snippets-hakyll/agda-snippets-hakyll.cabal: -------------------------------------------------------------------------------- 1 | name: agda-snippets-hakyll 2 | version: 0.1.2.2 3 | synopsis: Literate Agda support using agda-snippets, for Hakyll pages. 4 | description: This library provides basic functions to use in Hakyll web-pages to generate 5 | colourised and hyperlinked Agda source code snippets for literate Agda documents. 6 | . 7 | It makes use of the @agda-snippets@ library and will (hopefully) be kept up to 8 | date with the latest Agda versions. 9 | homepage: https://github.com/liamoc/agda-snippets#readme 10 | license: BSD3 11 | license-file: LICENSE 12 | author: Liam O'Connor 13 | maintainer: liamoc@cse.unsw.edu.au 14 | copyright: Liam O'Connor, 2015 15 | category: Dependent Types 16 | build-type: Simple 17 | cabal-version: >=1.10 18 | 19 | library 20 | build-depends: base >=4.7 && <4.10, agda-snippets >= 2.4.2.4 21 | , network-uri >= 2.6 && < 2.7 22 | , hakyll >= 4.6 && < 4.10 23 | , pandoc-types >=1.12 && <1.20 24 | , pandoc >= 1.13 && < 1.20 25 | , filepath >= 1.3 && < 1.5 26 | , directory >= 1.2 && < 1.4 27 | hs-source-dirs: src 28 | default-language: Haskell2010 29 | exposed-modules: Hakyll.Contrib.Agda 30 | 31 | source-repository head 32 | type: git 33 | location: https://github.com/liamoc/agda-snippets 34 | -------------------------------------------------------------------------------- /agda-snippets-hakyll/src/Hakyll/Contrib/Agda.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE LambdaCase #-} 2 | module Hakyll.Contrib.Agda 3 | ( -- * Literate Agda Compilers 4 | literateAgdaCompiler 5 | , literateAgdaCompilerWith 6 | , literateAgdaCompilerWithTransform 7 | , literateAgdaCompilerWithTransformM 8 | -- * Building Blocks 9 | , defaultFileType 10 | , readLiterateAgda 11 | -- * Command line options 12 | , CommandLineOptions (..) 13 | , defaultOptions 14 | ) where 15 | 16 | 17 | import Agda.Contrib.Snippets 18 | import Text.Pandoc.Options 19 | import Text.Pandoc.Definition 20 | import Hakyll.Core.Compiler 21 | import Hakyll.Core.Item 22 | import Hakyll.Core.Identifier 23 | import Hakyll.Web.Pandoc 24 | import Hakyll.Web.Pandoc.FileType 25 | import System.FilePath 26 | import System.Directory 27 | import Network.URI 28 | import Control.Exception 29 | 30 | -- | Like 'literateAgdaCompilerWith', but an arbitrary transformation of the Pandoc 31 | -- document can be added. 32 | literateAgdaCompilerWithTransform 33 | :: CommandLineOptions -- ^ Agda command line options 34 | -> FileType -- ^ Format to use when reading other, non-Agda blocks. 35 | -> URI -- ^ Base URI where external libraries can be found. 36 | -> ReaderOptions -- ^ Pandoc reader options 37 | -> WriterOptions -- ^ Pandoc writer options 38 | -> (Item Pandoc -> Item Pandoc) -- ^ Transformation to run 39 | -> Compiler (Item String) 40 | literateAgdaCompilerWithTransform opts ft uri ro wo 41 | = literateAgdaCompilerWithTransformM opts ft uri ro wo . (return .) 42 | 43 | -- | Like 'literateAgdaCompiler', but Pandoc options can be specified. 44 | literateAgdaCompilerWith 45 | :: CommandLineOptions -- ^ Agda command line options 46 | -> FileType -- ^ Format to use when reading other, non-Agda blocks. 47 | -> URI -- ^ Base URI where external libraries can be found. 48 | -> ReaderOptions -- ^ Pandoc reader options 49 | -> WriterOptions -- ^ Pandoc writer options 50 | -> Compiler (Item String) 51 | literateAgdaCompilerWith opts ft uri ro wo 52 | = literateAgdaCompilerWithTransform opts ft uri ro wo id 53 | 54 | -- | Compile a literate Agda document with the given Agda command line options, 55 | -- text block format type, and library uri for hyperlinks. 56 | literateAgdaCompiler 57 | :: CommandLineOptions -- ^ Agda command line options 58 | -> FileType -- ^ Format to use when reading other, non-Agda blocks. 59 | -> URI -- ^ Base URI where external libraries can be found. 60 | -> Compiler (Item String) 61 | literateAgdaCompiler opts ft uri 62 | = literateAgdaCompilerWith opts ft uri defaultHakyllReaderOptions defaultHakyllWriterOptions 63 | 64 | -- | Like 'literateAgdaCompilerWithTransform', but the transformation given is monadic. 65 | literateAgdaCompilerWithTransformM 66 | :: CommandLineOptions -- ^ Agda command line options 67 | -> FileType -- ^ Format to use when reading other, non-Agda blocks. 68 | -> URI -- ^ Base URI where external libraries can be found. 69 | -> ReaderOptions -- ^ Pandoc reader options 70 | -> WriterOptions -- ^ Pandoc writer options 71 | -> (Item Pandoc -> Compiler (Item Pandoc)) -- ^ Transformation to run 72 | -> Compiler (Item String) 73 | literateAgdaCompilerWithTransformM opts ft uri ro wo transform = 74 | fmap (writePandocWith wo) $ getResourceBody 75 | >>= readLiterateAgda opts uri 76 | >>= defaultFileType ft (readPandocWith ro) 77 | >>= transform 78 | 79 | -- | Run a function that might be part of your compiler pipeline, except that if the 80 | -- text format type cannot be detected from the extension (e.g, if it's a @lagda@ file), 81 | -- the specified file type will be used instead of 'Binary'. 82 | defaultFileType :: FileType -- ^ File type to default to 83 | -> (Item a -> Compiler (Item b)) -- ^ Pipeline function to run 84 | -> Item a 85 | -> Compiler (Item b) 86 | defaultFileType t act i = do 87 | let tau Binary = t 88 | tau x = x 89 | x <- act (i {itemIdentifier = fromFilePath $ fn -<.> extensionFor (tau $ fileType fn) }) 90 | return $ x {itemIdentifier = fromFilePath fn } 91 | where 92 | fn = toFilePath $ itemIdentifier i 93 | extensionFor = \case 94 | Binary -> takeExtension fn 95 | Css -> "css" 96 | DocBook -> "dbk" 97 | Html -> "html" 98 | LaTeX -> "tex" 99 | (LiterateHaskell f) -> extensionFor f ++ ".lhs" 100 | Markdown -> "md" 101 | MediaWiki -> "mediawiki" 102 | OrgMode -> "org" 103 | PlainText -> "txt" 104 | Rst -> "rst" 105 | Textile -> "textile" 106 | 107 | -- | Read a literate Agda document using the given options, producing a string with 108 | -- literate Agda snippets replaced with HTML, but everything else untouched. 109 | readLiterateAgda :: CommandLineOptions -- ^ Agda command line options 110 | -> URI -- ^ Base URI where external libraries can be found. 111 | -> Item String 112 | -> Compiler (Item String) 113 | readLiterateAgda aopt liburi i = 114 | if isAgda i 115 | then cached cacheName $ 116 | do fp <- getResourceFilePath 117 | unsafeCompiler $ bracket getCurrentDirectory setCurrentDirectory $ const $ 118 | do abfp <- canonicalizePath fp 119 | setCurrentDirectory (dropFileName abfp) 120 | s <- renderAgdaSnippets aopt "Agda" liburi abfp 121 | return $ i {itemBody = s} 122 | else return i 123 | where 124 | cacheName = "LiterateAgda.agdaCompiler" 125 | isAgda = (== ".lagda") . takeExtension . toFilePath . itemIdentifier 126 | -------------------------------------------------------------------------------- /agda-snippets/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Liam O'Connor (c) 2015 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Liam O'Connor nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /agda-snippets/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /agda-snippets/agda-snippets.cabal: -------------------------------------------------------------------------------- 1 | name: agda-snippets 2 | version: 2.5.2 3 | synopsis: Render just the Agda snippets of a literate Agda file to HTML 4 | description: This library provides a very simple function that translates just the code blocks 5 | of a literate Agda file to colourised, hyperlinked HTML. The output of this 6 | can then be run through Pandoc or other document processors to allow literate 7 | Agda to be comfortably written in any format that allows inline HTML snippets. 8 | . 9 | There is also a simple command-line application (@agda-snippets@) included 10 | that can be used as a standalone file processor. 11 | . 12 | The location of library source hyperlinks is configurable, as is the CSS class 13 | given to Agda code blocks. 14 | . 15 | This package is pinned to particular Agda versions, and therefore does not 16 | obey the PVP, as Agda does not. You should use whichever version of this library 17 | corresponds to the Agda version you wish to use. 18 | . 19 | The development version of this library, available from GitHub, may work with 20 | development versions of Agda, although it could be broken at any time. If you 21 | stick to stable versions, you should be fine. 22 | homepage: http://github.com/liamoc/agda-snippets#readme 23 | license: BSD3 24 | license-file: LICENSE 25 | author: Liam O'Connor 26 | maintainer: liamoc@cse.unsw.edu.au 27 | copyright: Liam O'Connor, 2015 28 | category: Dependent Types 29 | build-type: Simple 30 | -- extra-source-files: 31 | cabal-version: >=1.10 32 | 33 | library 34 | hs-source-dirs: src 35 | exposed-modules: Agda.Contrib.Snippets 36 | build-depends: base >= 4.8 && < 4.10 37 | , Agda == 2.5.2 38 | , xhtml >= 3000.2.1 && <3000.3 39 | , network-uri >= 2.6 && < 2.7 40 | , containers >= 0.5 && <0.6 41 | , mtl >= 2.1 && < 2.3 42 | default-language: Haskell2010 43 | 44 | executable agda-snippets 45 | hs-source-dirs: app 46 | main-is: Main.hs 47 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 48 | build-depends: base >= 4.7 && < 4.10 49 | , agda-snippets 50 | , Agda 51 | , network-uri 52 | , transformers >= 0.4 && < 0.6 53 | default-language: Haskell2010 54 | 55 | source-repository head 56 | type: git 57 | location: https://github.com/liamoc/agda-snippets 58 | -------------------------------------------------------------------------------- /agda-snippets/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE LambdaCase, CPP #-} 2 | module Main where 3 | 4 | import Agda.Contrib.Snippets 5 | import Agda.Interaction.Options 6 | import System.Environment 7 | import System.Exit 8 | import System.IO 9 | import Network.URI 10 | import Control.Applicative 11 | import Control.Monad.Trans.Except 12 | note :: b -> Maybe a -> Either b a 13 | note b Nothing = Left b 14 | note _ (Just a) = Right a 15 | 16 | #if MIN_VERSION_Agda(2,4,3) 17 | getOptions = runExceptT . parseStandardOptions 18 | #else 19 | getOptions = return . parseStandardOptions 20 | #endif 21 | 22 | main :: IO () 23 | main = getArgs >>= \case 24 | (fp:output:css:uri:agdaOpts) -> do 25 | opts <- getOptions agdaOpts 26 | case liftA2 (,) (note "Malformed URI" $ parseURIReference uri) 27 | opts of 28 | Right (uri', agdaOpts') -> writeFile output =<< renderAgdaSnippets agdaOpts' css uri' fp 29 | Left e -> hPutStrLn stderr e >> exitFailure 30 | _ -> do 31 | n <- getProgName 32 | putStrLn $ "Usage: " ++ n ++ " input-file.lagda output-file css-class-name /lib/uri/ [agda options]" 33 | exitFailure 34 | -------------------------------------------------------------------------------- /agda-snippets/src/Agda/Contrib/Snippets.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ViewPatterns #-} 2 | module Agda.Contrib.Snippets 3 | ( renderAgdaSnippets 4 | , CSSClass 5 | , CommandLineOptions (..) 6 | , defaultOptions 7 | ) where 8 | 9 | import Control.Monad.Except 10 | import Control.Monad.State (get) 11 | import Data.Char 12 | import Data.Function 13 | import Data.List 14 | import Data.Maybe 15 | import qualified Data.Map as Map 16 | import qualified Data.IntMap as IMap 17 | import Network.URI 18 | import System.Exit (exitFailure) 19 | import Text.XHtml.Strict 20 | 21 | import Agda.Interaction.Highlighting.Precise 22 | import qualified Agda.Interaction.Imports as Imp 23 | import Agda.Interaction.Options 24 | import Agda.Syntax.Abstract.Name (toTopLevelModuleName) 25 | import Agda.Syntax.Common 26 | import Agda.Syntax.Concrete.Name (TopLevelModuleName, moduleNameParts) 27 | import Agda.TypeChecking.Errors 28 | import Agda.TypeChecking.Monad (TCM) 29 | import qualified Agda.TypeChecking.Monad as TCM 30 | import Agda.Utils.FileName 31 | import qualified Agda.Utils.IO.UTF8 as UTF8 32 | import Agda.Utils.Lens 33 | 34 | 35 | checkFile :: AbsolutePath -> TCM TopLevelModuleName 36 | checkFile file = 37 | do TCM.resetState 38 | toTopLevelModuleName . TCM.iModuleName . fst <$> Imp.typeCheckMain file 39 | 40 | getModule :: TopLevelModuleName -> TCM (HighlightingInfo, String) 41 | getModule m = 42 | do Just mi <- TCM.getVisitedModule m 43 | Just f <- Map.lookup m . (^. TCM.stModuleToSource) <$> get 44 | s <- liftIO . UTF8.readTextFile . filePath $ f 45 | return (TCM.iHighlighting (TCM.miInterface mi), s) 46 | 47 | pairPositions :: HighlightingInfo -> String -> [(Int, String, Aspects)] 48 | pairPositions info contents = 49 | map (\cs@((mi, (pos, _)) : _) -> (pos, map (snd . snd) cs, fromMaybe mempty mi)) . 50 | groupBy ((==) `on` fst) . 51 | map (\(pos, c) -> (IMap.lookup pos infoMap, (pos, c))) . 52 | zip [1..] $ 53 | contents 54 | where 55 | infoMap = toMap (decompress info) 56 | 57 | -- TODO make these more accurate 58 | beginCode :: String -> Bool 59 | beginCode s = "\\begin{code}" `isInfixOf` s 60 | 61 | endCode :: String -> Bool 62 | endCode s = "\\end{code}" `isInfixOf` s 63 | 64 | infixEnd :: Eq a => [a] -> [a] -> [a] 65 | infixEnd i s = head [drop (length i) s' | s' <- tails s, i `isPrefixOf` s'] 66 | 67 | stripBegin :: (Int, String, Aspects) -> (Int, String, Aspects) 68 | stripBegin (i, s, mi) = (i, cut (dropWhile (== ' ') (infixEnd "\\begin{code}" s)), mi) 69 | where cut ('\n' : s') = s' 70 | cut s' = s' 71 | 72 | groupLiterate :: [(Int, String, Aspects)] 73 | -> [Either String [(Int, String, Aspects)]] 74 | groupLiterate contents = 75 | let (com, rest) = span (notCode beginCode) contents 76 | in Left ("\n\n" ++ concat [s | (_, s, _) <- com] ++ "\n\n") : go rest 77 | where 78 | go [] = [] 79 | go (be : mis) = 80 | let be'@(_, s, _) = stripBegin be 81 | (code, rest) = span (notCode endCode) mis 82 | in if "\\end{code}" `isInfixOf` s || "%" `isInfixOf` s 83 | then -- We simply ignore empty code blocks 84 | groupLiterate mis 85 | else Right (be' : code) : 86 | -- If there's nothing between \end{code} and \begin{code}, we 87 | -- start consuming code again. 88 | case rest of 89 | [] -> error "malformed file" 90 | ((_, beginCode -> True, _) : code') -> go code' 91 | (_ : com ) -> groupLiterate com 92 | 93 | notCode f (_, s, _) = not (f s) 94 | 95 | annotate :: URI -> TopLevelModuleName -> Int -> Aspects -> Html -> Html 96 | annotate libs m pos mi = anchor ! attributes 97 | where 98 | attributes = [name (show pos)] ++ 99 | fromMaybe [] (definitionSite mi >>= link) ++ 100 | (case classes of [] -> []; cs -> [theclass (unwords cs)]) 101 | 102 | classes = maybe [] noteClasses (note mi) ++ 103 | otherAspectClasses (otherAspects mi) ++ 104 | maybe [] aspectClasses (aspect mi) 105 | 106 | aspectClasses (Name mKind op) = 107 | let kindClass = maybe [] ((: []) . showKind) mKind 108 | 109 | showKind (Constructor Inductive) = "InductiveConstructor" 110 | showKind (Constructor CoInductive) = "CoinductiveConstructor" 111 | showKind k = show k 112 | 113 | opClass = ["Operator" | op] 114 | in kindClass ++ opClass 115 | aspectClasses a = [show a] 116 | 117 | otherAspectClasses = map show 118 | 119 | -- Notes are not included. 120 | noteClasses _ = [] 121 | 122 | link (m', pos') = if m == m' 123 | then Just [href ("#" ++ show pos')] 124 | else Just [href (show (tostdliblink m') ++ "#" ++ show pos')] 125 | tostdliblink mn = fromMaybe nullURI (parseURIReference (intercalate "." (moduleNameParts mn ++ ["html"]))) 126 | `nonStrictRelativeTo` libs 127 | 128 | renderFragments :: URI -> String 129 | -> TopLevelModuleName -> [Either String [(Int, String, Aspects)]] 130 | -> String 131 | renderFragments libs classpr m contents = 132 | concat [ case c of 133 | Left s -> s 134 | Right cs -> 135 | let h = pre . tag "code" . mconcat $ 136 | [ annotate libs m pos mi (stringToHtml s) 137 | | (pos, s, mi) <- cs ] 138 | in renderHtmlFragment (h ! [theclass classpr]) 139 | | c <- contents ] 140 | 141 | convert :: URI -> String -> TopLevelModuleName -> TCM String 142 | convert libs classpr m = 143 | do (info, contents) <- getModule m 144 | return . renderFragments libs classpr m . groupLiterate . pairPositions info $ contents 145 | 146 | -- | The CSS Class to use for Agda code blocks 147 | type CSSClass = String 148 | 149 | -- | Render a literate Agda module's code snippets to HTML. 150 | renderAgdaSnippets 151 | :: CommandLineOptions -- ^ Agda Command line options 152 | -> CSSClass -- ^ CSS Class name 153 | -> URI -- ^ URI where other Agda HTML listings are found (for library links etc) 154 | -> FilePath -- ^ File name of literate agda file 155 | -> IO String -- ^ Returns the file contents as a string, where each code block has been rendered to HTML. 156 | renderAgdaSnippets opts classpr libs fp = 157 | do afp <- absolute fp 158 | r <- TCM.runTCMTop $ catchError (TCM.setCommandLineOptions opts >> 159 | checkFile afp >>= convert libs classpr) 160 | $ \err -> do s <- prettyError err 161 | liftIO (putStrLn s) 162 | throwError err 163 | case r of 164 | Right s -> return (dropWhile isSpace s) 165 | Left _ -> exitFailure 166 | 167 | -------------------------------------------------------------------------------- /stack-travis.yaml: -------------------------------------------------------------------------------- 1 | # For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md 2 | 3 | # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) 4 | resolver: lts-8.5 5 | 6 | # Local packages, usually specified by relative directory name 7 | # I've added my local installation of development Agda here. Change it to your development Agda path. 8 | packages: 9 | - 'agda-snippets' 10 | - 'agda-snippets-hakyll' 11 | # - '../../../langs/agda' 12 | 13 | # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3) 14 | extra-deps: [] 15 | 16 | # Override default flag values for local packages and extra-deps 17 | flags: {} 18 | 19 | # Extra package databases containing global packages 20 | extra-package-dbs: [] 21 | 22 | # Control whether we use the GHC we find on the path 23 | # system-ghc: true 24 | 25 | # Require a specific version of stack, using version ranges 26 | # require-stack-version: -any # Default 27 | # require-stack-version: >= 0.1.4.0 28 | 29 | # Override the architecture used by stack, especially useful on Windows 30 | # arch: i386 31 | # arch: x86_64 32 | 33 | # Extra directories used by stack for building 34 | # extra-include-dirs: [/path/to/dir] 35 | # extra-lib-dirs: [/path/to/dir] 36 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md 2 | 3 | # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) 4 | resolver: lts-8.5 5 | 6 | # Local packages, usually specified by relative directory name 7 | # I've added my local installation of development Agda here. Change it to your development Agda path. 8 | packages: 9 | - 'agda-snippets' 10 | - 'agda-snippets-hakyll' 11 | # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3) 12 | extra-deps: [] 13 | 14 | # Override default flag values for local packages and extra-deps 15 | flags: {} 16 | 17 | # Extra package databases containing global packages 18 | extra-package-dbs: [] 19 | 20 | # Control whether we use the GHC we find on the path 21 | # system-ghc: true 22 | 23 | # Require a specific version of stack, using version ranges 24 | # require-stack-version: -any # Default 25 | # require-stack-version: >= 0.1.4.0 26 | 27 | # Override the architecture used by stack, especially useful on Windows 28 | # arch: i386 29 | # arch: x86_64 30 | 31 | # Extra directories used by stack for building 32 | # extra-include-dirs: [/path/to/dir] 33 | # extra-lib-dirs: [/path/to/dir] 34 | --------------------------------------------------------------------------------