hyper-literate.scrbl (11510B)
1 #lang scribble/manual 2 @require[racket/require 3 @for-label[hyper-literate 4 racket/base 5 (subtract-in scribble/manual hyper-literate) 6 racket/contract]] 7 8 @title{Hyper-literate programming} 9 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] 10 11 @(require scribble/manual 12 scribble/core 13 scribble/decode 14 scribble/racket 15 (only-in scribble/racket value-link-color)) 16 17 @defmodulelang[hyper-literate] 18 19 The @racketmodname[hyper-literate] metalanguage extends the 20 features of @racketmodname[scribble/lp2], with the goal of 21 providing a more modern view on literate programming. It can 22 be parameterized with the language used in the chunks (so 23 that it is possible to directly write 24 @racketmodname[typed/racket] programs with 25 @racketmodname[hyper-literate], for example). 26 27 On the first line, which begins with @tt{@litchar{#lang} 28 @racketmodname[hyper-literate]}, the language recognises the following 29 options: 30 31 @(require scribble/core 32 (only-in scribble/private/manual-vars boxed-style) 33 scribble/private/manual-utils) 34 @(make-table 35 boxed-style 36 (list 37 (list 38 @paragraph[(style #f '())]{ 39 @tt{@litchar{#lang} @racketmodname[hyper-literate] @racket[_lang] 40 @racket[_maybe-no-req] @racket[_maybe-no-auto]}}) 41 flow-empty-line 42 (list 43 @racketgrammar*[ 44 (maybe-no-req (code:line) 45 (code:line #:no-require-lang)) 46 (maybe-no-auto (code:line) 47 (code:line #:no-auto-require))]))) 48 49 where @racket[_lang] is a module name which can be used as 50 a @litchar{#lang}, for example @racketmodname[typed/racket] 51 or @racketmodname[racket/base]. 52 53 The current implementation of hyper-literate needs to inject 54 a @racket[(require _lang)] in the expanded module, in order 55 to have the arrows properly working in DrRacket for 56 "built-in" identifiers which are provided by the 57 @racket[_lang] itself. The @racket[require] statement is 58 injected after the whole ``code'' module has been expanded. 59 It is worth noting that an extra scope is added to the expanded 60 body of the module, in order to make any @racket[require] form 61 within more specific than the @racket[(require _lang)]. 62 63 The current implementation of @racketmodname[scribble/lp2], 64 on which @racketmodname[hyper-literate] relies (with a few 65 changes), extracts the @racket[require] statements from 66 chunks of code, and passes them to 67 @racket[(require (for-label …))]. The goal is to have 68 identifiers from required modules automatically highlighted 69 and hyperlinked to their documentation. However, all 70 meta-levels are smashed into the @racket[#f], i.e. 71 @racket[for-label] meta-level. As a consequence, conflicts 72 can arise at the @racket[for-label] meta-level between two 73 modules, even if these two modules were originally required 74 at distinct meta-levels in the source program. It is 75 possible in this case to disable the feature using 76 @racket[#:no-auto-require], and to manually call 77 @racket[(require (for-label …))] and handle conflicting 78 identifiers in a more fine-grained way. 79 80 @deprecated[#:what @racket[#:no-require-lang] ""]{ 81 82 The @racket[#:no-require-lang] is deprecated starting from version 0.1, and 83 is not needed anymore. It is still accepted for backwards compatibility. Note 84 that version 0.1 of this library requires a fairly recent Racket version to 85 work properly (it needs v.6.7.0.4 with the commit 86 @tt{8a7852ebbfeb85a8f860700ba5ae481ed7aa9b41}, or v.6.7.0.5 or later). By 87 default, raco will install v0.0 of hyper-literate on older Racket versions. 88 89 The extra @racket[require] statement injected by 90 @racketmodname[hyper-literate] could in previous versions conflict with 91 user-written @racket[require] statements. These @racket[require] statements 92 can shadow some built-ins, and this case would yield conflicts. The 93 @racket[#:no-require-lang] option disables that behaviour in versions < 0.1, 94 and has the only drawback that built-ins of the @racket[_lang] language do not 95 have an arrow in DrRacket (but they still should be highlighted with -a 96 turquoise background when hovered over with the mouse).} 97 98 @section{What is hyper-literate programming?} 99 100 Hyper-literate programming is to literate programming 101 exactly what hypertext documents are to regular books and 102 texts. Literate programming is about telling other 103 programmers how the program works (instead of just telling 104 the compiler what it does). Telling this story can be done 105 using non-linear, hyperlinked documents. 106 107 For now these utilities only help with manipulating literate 108 programming chunks (e.g. repeating the same chunk in several 109 places in the output document, but keeping a single copy in 110 the source code). 111 112 Ultimately, the reading experience should be closer to 113 viewing an interactive presentation, focusing on the parts 114 of the program that are of interest to you: expand on-screen 115 the chunks you are curious about, run some tests and see 116 their result, etc. 117 118 @itemlist[ 119 @item{Imagine something like 120 @hyperlink["http://www.andrewbragdon.com/codebubbles_site.asp"]{ 121 code bubbles}, but with explanatory text coming along 122 with the source code.} 123 @item{Imagine something like 124 @hyperlink["http://inform7.com/"]{Inform}, but focused on 125 exploring a program instead of exploring an imaginary 126 world — after all, a program is some kind of imaginary 127 world.}] 128 129 @section{Chunks of code} 130 131 @; @racket[chunk] does not work for these, probably due to the use of either: 132 @; @title[#:tag "lp" …]{Literate Programming} 133 @; or: 134 @; @defmodulelang[scribble/lp2 #:use-sources (scribble/lp)]{…} 135 @; in scribble-doc/scribblings/scribble/lp.scrbl 136 @; See scribble bug #51 https://github.com/racket/scribble/issues/51 137 @(define scribble-chunk 138 (element symbol-color 139 (make-link-element value-link-color 140 (decode-content (list "chunk")) 141 '(form ((lib "scribble/lp.rkt") chunk))))) 142 @(define scribble-CHUNK 143 (element symbol-color 144 (make-link-element value-link-color 145 (decode-content (list "CHUNK")) 146 '(form ((lib "scribble/lp.rkt") CHUNK))))) 147 148 @;{ 149 @(module scribble-doc-links racket/base 150 (require scribble/manual 151 (for-label scribble/lp2 152 scribble/private/lp)) 153 (provide (all-defined-out)) 154 (define scribble-chunk @racket[chunk]) 155 (define scribble-CHUNK @racket[CHUNK])) 156 @(require 'scribble-doc-links) 157 } 158 159 @defform[(chunk <name> content ...)]{ 160 Same as @scribble-chunk from @racketmodname[scribble/lp2], 161 with a few tweaks and bug fixes.} 162 163 @defform[(CHUNK <name> content ...)]{ 164 Same as @scribble-CHUNK from @racketmodname[scribble/lp2], 165 with a few tweaks and bug fixes.} 166 167 @section{Memorizing and repeating chunks} 168 169 @defform[(defck <name> content ...)]{ 170 Like @racket[chunk] from @racketmodname[scribble/lp2], but 171 remembers the chunk so that it can be re-displayed later 172 using @racket[repeat-chunk].} 173 174 @defform[(repeat-chunk <name>)]{ 175 Shows again a @racket[chunk] of code previously remembered 176 with @racket[defck]. If the @racket[<name>] starts and 177 ends with angle brackets, they are replaced by parentheses 178 to hint that this is not the first occurrence of this 179 chunk, so that the name becomes @racket[|(name)|]} 180 181 @section{Order of expansion of the program} 182 183 The file is expanded a first time, in order to identify and 184 aggregate the chunks of code (declared with @racket[chunk]). 185 Then, the top-level module of the file is constructed using 186 these chunks, and a @racket[doc] submodule is added, which 187 contains all the surrounding text. The chunks are typeset 188 where they appear using @racket[racketblock]. 189 190 The @racket[doc] submodule is declared using 191 @racket[module*], so that it can use 192 @racket[(require (submod ".."))] to use functions declared 193 in the chunks. For example, it should be possible to 194 dynamically compute the result of a function, and to insert 195 it into the document, so that the value displayed always 196 matches the implementation. 197 198 When the file is expanded for the first time, however, the 199 @racket[(submod "..")] does not exist yet, and cannot be 200 required. This is the case because the first expansion is 201 performed precisely to extract the chunks and inject them in 202 that module. 203 204 To solve this problem, the following macros behave 205 differently depending on whether the code is being expanded 206 for the first time or not (in which case the 207 @racket[(submod "..")] module can be used). 208 209 @defform[(if-preexpanding a b)]{ 210 Expands to @racket[a] if the code is being pre-expanded, 211 and expands to @racket[b] if the @racket[(submod "..")] 212 module can be used.} 213 214 @defform[(when-preexpanding . body)]{ 215 Expands to @racket[(begin . body)] if the code is being 216 pre-expanded, and expands to @racket[(begin)] otherwise.} 217 218 @defform[(unless-preexpanding . body)]{ 219 Expands to @racket[(begin . body)] if the @racket[(submod "..")] 220 module can be used, and expands to @racket[(begin)] otherwise.} 221 222 @section{A note on literate programs as subsections of another document} 223 224 To use @racket[include-section] on hyper-literate programs, a couple of 225 workarounds are required to avoid issues with duplicate tags for 226 identically-named chunks (like @racket[<*>], which is likely to always be 227 present). 228 229 @defparam[chunks-toc-prefix prefix-list (listof string?)]{ 230 We give an example for two files which are part of a hypothetical 231 @elem[#:style 'tt "pkg"] package: 232 233 @itemlist[ 234 @item{The main scribble file @filepath{main.scrbl} in the 235 @filepath{scribblings} sub-directory includes the hyper-literate file 236 @filepath{program.hl.rkt} located in the package's root directory, one 237 directory level above: 238 239 @codeblock[#:keep-lang-line? #t 240 "#lang scribble/manual\n" 241 "@title{Main document title}\n" 242 "@include-section{../program.hl.rkt}\n" 243 "@; could include other hyper-literat programs here\n"]} 244 @item{To avoid issues with duplicate tag names, it is necessary to use the 245 @racket[#:tag-prefix] option on the hyper literate program's @racket[title]. 246 Unfortunately, this breaks links to chunks in the table of contents, because 247 scribble does not automatically add the correct prefix to them. To ensure 248 that the links correctly work in the table of contents, it is necessary to 249 tell hyper-literate what is the chain of document includes. The whole 250 @filepath{program.hl.rkt} file will be: 251 252 @codeblock[#:keep-lang-line? #t 253 "#lang hyper-literate racket/base\n" 254 "@title[#:tag-prefix '(lib \"pkg/program.hl.rkt\")]{Program title}\n" 255 "@(chunks-toc-prefix '(\"(lib pkg/scribblings/main.scrbl)\"\n" 256 " \"(lib pkg/program.hl.rkt)\"))\n" 257 "@chunk[<*>\n" 258 " 'program-code-here]\n"] 259 260 Note that the argument for the @racket[chunks-toc-prefix] parameter is a list 261 of string, and the strings are representations of module paths. The 262 occurrences of @racket[lib] above are not symbols, they are just part of the 263 string. Compare this with the following, which would be incorrect: 264 265 @codeblock[#:keep-lang-line? #t 266 "#lang hyper-literate racket/base\n" 267 "@title[#:tag-prefix '(lib \"pkg/program.hl.rkt\")]{Program title}\n" 268 "@; This is incorrect:\n" 269 "@(chunks-toc-prefix '((lib \"pkg/scribblings/main.scrbl\")\n" 270 " (lib \"pkg/program.hl.rkt\")))\n" 271 "@chunk[<*>\n" 272 " 'program-code-here]\n"]}]} 273 274 @include-section[(submod (lib "hyper-literate/scribblings/diff1-example.hl.rkt") 275 doc)]